Private members
As of .NET 8.0, Mapperly supports mapping members that are normally inaccessible like private
or protected
properties.
This is made possible by using the UnsafeAccessorAttribute which lets Mapperly access normally inaccessible members with zero overhead while being completely AOT safe.
By default IncludedMembers
and IncludedConstructors
is set to MemberVisibility.AllAccessible
which will configure Mapperly to map members of all accessibility levels as long as they are ordinarily accessible.
To enable unsafe accessor usage, set IncludedMembers
and/or IncludedConstructors
to MemberVisibility.All
.
Mapperly will then try to map members of all accessibilities, including ones that are not usually visible to external types.
IncludedConstructors
can be used separately from IncludedMembers
.
This allows you to use inaccessible constructors but only map accessible members or vice versa.
- Declaration
- Generated code
[Mapper(
IncludedMembers = MemberVisibility.All,
IncludedConstructors = MemberVisibility.All)]
public partial class FruitMapper
{
public partial FruitDto ToDto(Fruit source);
}
public class Fruit
{
private bool _isSeeded;
public string Name { get; set; }
private int Sweetness { get; set; }
}
public class FruitDto
{
private FruitDto() {}
private bool _isSeeded;
public string Name { get; set; }
private int Sweetness { get; set; }
}
Mapperly generates a file scoped class containing an accessor method for each member which cannot be accessed directly. Mapperly then uses these methods to create the instance, get and set the members as needed. Note that this uses zero reflection and is as performant as using an ordinary property or field.
public partial class FruitMapper
{
private partial global::FruitDto ToDto(global::Fruit source)
{
var target = UnsafeAccessor.CreateFruitDto();
target.GetIsSeeded1() = source.GetIsSeeded();
target.Name = source.Name;
target.SetSweetness(source.GetSweetness());
return target;
}
}
static file class UnsafeAccessor
{
[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Constructor)]
public static extern global::FruitDto CreateFruitDto(this global::FruitDto target);
[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Field, Name = "_isSeeded")]
public static extern ref bool GetSeeded(this global::Fruit target);
[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Field, Name = "_isSeeded")]
public static extern ref bool GetSeeded1(this global::FruitDto target);
[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "get_Sweetness")]
public static extern int GetSweetness(this global::Fruit source);
[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set_Sweetness")]
public static extern void SetSweetness(this global::FruitDto target, int value);
}
Controlling member accessibility
In addition to mapping inaccessible members,
MemberVisbility
can be used to control which members are considered, depending on their accessibility modifier.
For instance MemberVisibility.Private | MemberVisibility.Protected
will cause Mapperly to only consider private and protected members,
generating an unsafe accessor if needed.
IncludedConstructors
can be used separately from IncludedMembers
.
This allows you to use inaccessible constructors but only map accessible members or vice versa.
[Mapper(IncludedMembers = MemberVisibility.Private | MemberVisibility.Protected)]
public partial class FruitMapper
{
public partial FruitDto ToDto(Fruit source);
}