映射两个相同类型的对象(不包括某些字段)的最佳方法是什么?

我之前在这里发布了我的问题,但我没有得到任何回复原因 – 我猜 – 它太通用了。 我会试着更简洁。

我有两个相同类型的对象,我想映射一些属性并排除其他属性。 我想要做的是将对象保存在缓存中并稍后使用具有特定属性的属性(字段)获取它。

我看过Automapper,但是我没有找到任何适合我的东西,所以我想要实现我自己的系统。
我创建了一个属性:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public class FilterFieldAttribute: Attribute { } 

并在我需要包含的字段上装饰了一个类:

 public class OrdersViewModel : BaseViewModel { ... [FilterField] [DisplayName("Order Number:")] public string OrderNumber { get; set; } [FilterField] [DisplayName("From date:")] public DateTime FromDate { get; set; } [FilterField] [DisplayName("To date:")] public DateTime ToDate { get; set; } [DisplayName("Status:")] public int Status { get; set; } ... } 

现在,我已经实现了一个负责映射的函数:

  private T Map(T Source, T Destination) where T : BaseViewModel { if (Source == null) { return (Source); } FilterFieldAttribute filterAttribute; foreach (PropertyInfo propInfo in typeof(T).GetProperties()) { foreach (FilterFieldAttribute attr in propInfo.GetCustomAttributes(typeof(FilterFieldAttribute), false)) { filterAttribute = attr as FilterFieldAttribute; if (filterAttribute != null) { var value = propInfo.GetValue(Source, null); propInfo.SetValue(Destination, value, null); } } } return (Destination); } 

现在,当我需要从缓存中获取我的viewmodel并仅填充标有该属性的属性时,我的代码如下所示:

 viewModel = Map(myCache.Get(Key) as T, viewModel); 

我不知道这是否是最好的,但这似乎是我找到的唯一方法。
任何建议将不胜感激。

使用直接reflection(如示例中)将是sloooow ; 给出一个更完整的答案是棘手的,因为它取决于你是否需要浅层克隆或深层克隆子对象。 无论哪种方式,您都应该期望这涉及一些元编程和缓存 – 使用ILGenerator或Expression。

但是,对于惰性选项,序列化可能很有用。 许多序列化程序允许您使用属性来包含/排除特定项目; 通过序列化到内存流,倒带( .Position=0 )和反序列化你应该得到你所选成员的深层副本。

以下是使用Expression的示例:

 using System; using System.ComponentModel; using System.Linq; using System.Linq.Expressions; [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public class FilterFieldAttribute: Attribute { public static T Clone(T obj) where T : class, new() { return Cache.clone(obj); } private static class Cache where T : class, new() { public static readonly Func clone; static Cache() { var param = Expression.Parameter(typeof(T), "source"); var members = from prop in typeof(T).GetProperties() where Attribute.IsDefined(prop, typeof(FilterFieldAttribute)) select Expression.Bind(prop, Expression.Property(param, prop)); var newObj = Expression.MemberInit(Expression.New(typeof(T)), members); clone = Expression.Lambda>(newObj, param).Compile(); } } } public class OrdersViewModel { [FilterField] [DisplayName("Order Number:")] public string OrderNumber { get; set; } [FilterField] [DisplayName("From date:")] public DateTime FromDate { get; set; } [FilterField] [DisplayName("To date:")] public DateTime ToDate { get; set; } [DisplayName("Status:")] public int Status { get; set; } static void Main() { var foo = new OrdersViewModel { OrderNumber = "abc", FromDate = DateTime.Now, ToDate = DateTime.Now, Status = 1}; var bar = FilterFieldAttribute.Clone(foo); } } 

听起来是一个好的开始,但你正在失去AutoMapper的这么多好处。 AutoMapper还可以处理您在此处没有的嵌套属性(当您的类包含嵌套的映射类时)。

由于AutoMapper是一个oprn源项目,我建议您使用AutoMapper源并在那里实现过滤。 事实上,这对其他人来说也是有益的。

我认为你的方法还可以。 反思有一些性能影响 – 值得考虑。

另一种高效且简单的方法可能是让BaseViewModel定义一个抽象方法:

 public abstract BaseViewModel ToCacheVersion(); 

哪个可用于将子类转换为正确的类型。 每个子类都会处理自己的映射:

 public class ViewModelX { public ViewModelX(string name, string description) { Name = name; Description = description; } ... public override BaseViewModel ToCacheVersion() { return new ViewModelX( Name, // Include the name. null // Ignore the description. ); } ... }