
Mapster 映射非公开成员完全指南从 EnableNonPublicMembers 到细粒度访问修饰符控制【免费下载链接】MapsterA fast, fun and stimulating object to object Mapper项目地址: https://gitcode.com/GitHub_Trending/ma/MapsterMapster 默认只映射类型的公开public成员但在处理遗留实体、ORM 代理类或带有private set属性、私有字段的 DTO 时往往需要将非公开成员也纳入映射范围。本文围绕 Mapster 官方文档 Mapping non-public members 展开系统讲解EnableNonPublicMembers、AdaptMember特性、Map与IncludeMember四种映射非公开成员的途径并结合仓库源码与测试用例说明其底层判定逻辑与适用边界帮助你按需、安全地打开非公开成员映射能力。默认行为非公开成员默认不参与映射在开始配置之前先明确 Mapster 的默认策略非公开成员private / protected / internal / protected internal默认不会被映射。这一行为有测试用例直接背书——在 WhenMappingPrivateFieldsAndProperties.cs 中Default_Settings_Should_Not_Map_Private_Fields_To_New_Object默认配置下私有字段_id不会被写入目标 DTODefault_Settings_Should_Not_Map_Private_Properties_To_New_Object默认配置下私有属性Name同样不会被映射。因此要让非公开成员参与映射必须显式地进行配置。Mapster 共提供四种途径下文逐一展开。方式一EnableNonPublicMembers扩展方法EnableNonPublicMembers(bool)是最直接的开关置为true后Mapster 将允许对所有非公开成员进行读写映射。// 类型对级别仅作用于 Poco - Dto 这一对类型 TypeAdapterConfigPoco, Dto.NewConfig().EnableNonPublicMembers(true); // 全局级别作用于所有类型对 TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMembers(true);从源码看该方法的核心实现只是把布尔值写入设置项 TypeAdapterSetter.cspublic static TSetter EnableNonPublicMembersTSetter(this TSetter setter, bool value) where TSetter : TypeAdapterSetter { setter.CheckCompiled(); setter.Settings.EnableNonPublicMembers value; return setter; }该设置真正发挥作用的位置在成员筛选逻辑 ReflectionUtils.cs 的ShouldMapMember中当EnableNonPublicMembers true时Mapster 会调用Mapster.ShouldMapMember.AllowNonPublic谓词对成员放行。底层判定AllowNonPublic谓词AllowNonPublic定义于 ShouldMapMember.cspublic static readonly FuncIMemberModel, MemberSide, bool? AllowNonPublic (model, _) (model.AccessModifier AccessModifier.NonPublic) 0 ? (bool?) null : !(model.Info is FieldInfo) || !model.HasCustomAttributeCompilerGeneratedAttribute();它包含两条关键规则只影响非公开成员AccessModifier不带NonPublic标志的成员返回null即不干预、交给后续逻辑决定排除编译器生成字段对于带有CompilerGeneratedAttribute的字段典型如自动属性背后的 backing field即使是非公开也不会被映射避免破坏属性映射的正常流程。访问修饰符的判定来源成员的AccessModifier由 ReflectionUtils.cs 中的GetAccessModifier重载根据反射信息换算得出IsPublic对应PublicIsFamily对应ProtectedIsAssembly对应InternalIsFamilyOrAssembly对应ProtectedInternal其余归为Private。这为下文IncludeMember按修饰符过滤提供了数据基础。TwoWays 支持EnableNonPublicMembers同样支持双向配置。在 TypeAdapterSetter.cs 中TwoWaysTypeAdapterSetterTSource, TDestination会将该设置同时应用到 Source→Destination 与 Destination→Source 两个方向。方式二AdaptMember特性当不想全局放开非公开成员、只希望针对个别成员映射时可以使用AdaptMember特性按成员精确指定public class Product { [AdaptMember] private string HiddenId { get; set; } public string Name { get; set; } }AdaptMember定义于 AdaptMemberAttribute.cs特性本身允许作用于字段、属性、构造参数三种目标AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property并提供两个可选属性Name指定成员在映射时使用的名称可用于改名映射如[AdaptMember(Id)] private string HiddenIdSide限定该特性只对 Source 侧或 Destination 侧生效MemberSide枚举不指定则两侧都生效。在底层ShouldMapMember.cs 中的AllowAdaptMember谓词会检查成员是否携带AdaptMemberAttribute命中即返回true放行同样遵循Side匹配规则。这意味着即使没有开启EnableNonPublicMembers被[AdaptMember]标注的非公开成员也能单独参与映射。方式三Map扩展方法按名称映射私有成员如果目标成员与源成员名称不同可以借助Map扩展方法通过指定成员名完成对私有成员的映射TypeAdapterConfigTSource, TDestination .NewConfig() .Map(PrivateDestName, PrivateSrcName);第一个参数是目标成员名第二个参数是源成员名。底层实现位于 TypeAdapterSetter.cs该方法向Settings.Resolvers追加一个InvokerModel携带DestinationMemberName与SourceMemberName在编译阶段作为解析器参与映射表达式的构建从而绕过默认的成员筛选直接建立“名称到名称”的赋值通道。方式四IncludeMember精确控制访问修饰符EnableNonPublicMembers是一刀切开关而IncludeMember允许你用谓词自定义哪些成员可以映射典型用法是按访问修饰符过滤TypeAdapterConfig.GlobalSettings.Default .IncludeMember((member, side) member.AccessModifier AccessModifier.Internal || member.AccessModifier AccessModifier.ProtectedInternal);上面的配置意味着只有internal与protected internal成员参与映射而private、protected等其余非公开成员仍保持默认不映射。IncludeMember的签名是FuncIMemberModel, MemberSide, boolmember参数暴露成员模型含上文提到的AccessModifier属性side参数标明当前成员处于 Source 还是 Destination 侧因此你完全可以写出更复杂的条件例如“仅当在目标侧时才允许 internal 属性”。实现上TypeAdapterSetter.cs 将谓词包装进Settings.ShouldMapMember列表谓词返回true时成员被放行返回false则被忽略返回null即谓词不命中则交给后续规则继续判断。它与EnableNonPublicMembers是叠加生效的关系适合在全局开关的基础上做二次收窄。对应的测试用例 WhenMappingPrivateFieldsAndProperties.cs 演示了IncludeMember((model, side) model.AccessModifier AccessModifier.Protected)的用法验证了 protected 属性在开启该谓词后可以正确映射。重要注意事项无公共属性的类型会被当作原始类型官方文档特别强调了一个容易踩坑的场景见 Mapping non-public members 的 Note 一节如果类型不包含任何公共属性Mapster 会将该类型当作原始类型primitive处理此时必须显式声明类型对才能确保非公开成员映射真正生效。TypeAdapterConfig.GlobalSettings.Default.EnableNonPublicMembers(true); TypeAdapterConfigPrivatePoco, PrivateDto.NewConfig();原因在于 Mapster 判断类型是否可映射时依赖其可访问成员。若一个类型没有任何公共可映射成员Mapster 会将其视为类似string、int的“基本值”走直接赋值路径而非对象映射路径可参考 ReflectionUtils.cs 中IsPrimitiveKind对可转换基础类型的判定思路。因此先通过EnableNonPublicMembers(true)类型对或全局均可开启非公开成员支持再显式调用TypeAdapterConfigPrivatePoco, PrivateDto.NewConfig()注册类型对让 Mapster 以对象映射的方式处理这一对类型。两者缺一不可否则配置不会如预期生效。测试验证端到端行为一览仓库中的 WhenMappingPrivateFieldsAndProperties.cs 覆盖了非公开成员映射的主要行为可作为配置正确性的参照测试方法验证点Default_Settings_Should_Not_Map_Private_Fields_To_New_Object默认不映射私有字段Default_Settings_Should_Not_Map_Private_Properties_To_New_Object默认不映射私有属性Should_Map_Private_Field_To_New_Object_CorrectlyEnableNonPublicMembers(true)后私有字段正确映射Should_Map_Private_Property_To_New_Object_CorrectlyEnableNonPublicMembers(true)后私有属性正确映射Should_Map_To_Private_Fields_Correctly反向映射公开 DTO → 私有字段目标Should_Map_To_Private_Properties_Using_IncludeIncludeMember谓词放行 protected 属性Test_Dictionary字典源数据经EnableNonPublicMembers映射到目标的私有属性其中Test_Dictionary场景值得注意它展示了ForTypeIDictionarystring, object, Pet().EnableNonPublicMembers(true)的用法即字典键Color可以写入Pet的私有属性Color验证了非公开成员映射在字典驱动场景下同样生效。总结四种方式的选用建议EnableNonPublicMembers(true)全局或类型对级别的总开关适合整体放开非公开成员映射搭配“显式注册类型对”应对全私有类型[AdaptMember]特性细粒度定点映射适合仅个别私有成员需要映射、且希望保持默认策略不变的场景还支持Name改名与Side方向限定Map(dest, src)名称不一致时的显式桥接通过解析器直接绑定源/目标成员名IncludeMember谓词在总开关之外做访问修饰符级别的精确筛选例如只允许internal与protected internal成员参与映射。理解ShouldMapMember与AllowNonPublic的判定顺序ShouldMapMember.cs、ReflectionUtils.cs能帮助你预测配置叠加后的实际行为避免在遗留系统或封闭类型迁移时踩到“映射静默丢失”的坑。【免费下载链接】MapsterA fast, fun and stimulating object to object Mapper项目地址: https://gitcode.com/GitHub_Trending/ma/Mapster创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考