将一个对象属性值转移到另一个对象属性值

最重要的是,我知道AutoMapper ,我不想使用它。 因为我正在学习C# ,我想深入了解它。 所以我正试着自己做这个问题(下面解释)。

但是,我正在尝试创建一个属性复制器来处理一种类型属性的值到另一种属性,如果该属性具有相同的名称和类型,并且可以从源中读取并在目标中可写。 我正在使用type.GetProperties()方法。 采样方法如下:

  static void Transfer(object source, object target) { var sourceType = source.GetType(); var targetType = target.GetType(); var sourceProps = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance); var targetProps = (from t in targetType.GetProperties() where t.CanWrite && (t.GetSetMethod().Attributes & MethodAttributes.Static) == 0 select t).ToList(); foreach(var prop in sourceProps) { var value = prop.GetValue(source, null); var tProp = targetProps .FirstOrDefault(p => p.Name == prop.Name && p.PropertyType.IsAssignableFrom(prop.PropertyType)); if(tProp != null) tProp.SetValue(target, value, null); } } 

它有效,但我在SO上读到了一个答案,即使用System.Reflection.EmitILGenerator以及后期委托更快,性能更高。 但没有更多的解释或任何链接。 你能帮我理解加速这段代码的方法吗? 或者你能告诉我一些关于EmitILGenerator后期代表的链接吗? 或者你认为有什么能帮到我? 提前致谢。

COMPELETE Q:

我从@ svick的回答中了解并学到很多东西。 但是现在,如果我想将它用作开放式通用方法,我该怎么做呢? 像这样的东西:

 public TTarget Transfer(TSource source) where TTarget : class, new() { } 

或者延期:

 public static TTarget Transfer(this TSource source) where TTarget : class, new() { } 

可以使用Reflection.Emit来执行此操作,但使用Expression通常会更容易,并且它为您提供基本相同的性能。 请记住,仅当您缓存已编译的代码时才会有性能优势,例如在Dictionary, Action> ,我在这里没有这样做。

 static void Transfer(object source, object target) { var sourceType = source.GetType(); var targetType = target.GetType(); var sourceParameter = Expression.Parameter(typeof(object), "source"); var targetParameter = Expression.Parameter(typeof(object), "target"); var sourceVariable = Expression.Variable(sourceType, "castedSource"); var targetVariable = Expression.Variable(targetType, "castedTarget"); var expressions = new List(); expressions.Add(Expression.Assign(sourceVariable, Expression.Convert(sourceParameter, sourceType))); expressions.Add(Expression.Assign(targetVariable, Expression.Convert(targetParameter, targetType))); foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (!property.CanRead) continue; var targetProperty = targetType.GetProperty(property.Name, BindingFlags.Public | BindingFlags.Instance); if (targetProperty != null && targetProperty.CanWrite && targetProperty.PropertyType.IsAssignableFrom(property.PropertyType)) { expressions.Add( Expression.Assign( Expression.Property(targetVariable, targetProperty), Expression.Convert( Expression.Property(sourceVariable, property), targetProperty.PropertyType))); } } var lambda = Expression.Lambda>( Expression.Block(new[] { sourceVariable, targetVariable }, expressions), new[] { sourceParameter, targetParameter }); var del = lambda.Compile(); del(source, target); } 

如果你有这个,写你的通用方法是简单的:

 public TTarget Transfer(TSource source) where TTarget : class, new() { var target = new TTarget(); Transfer(source, target); return target; } 

将main worker方法设置为generics并创建Action ,甚至让它直接创建对象并使用Func 。 但是,如果按照我的建议添加缓存,则意味着您必须使用类似Dictionary, Delegate>并在从缓存中检索委托后将委托转换为正确的类型。

您可能只考虑获取与目标匹配的属性(按名称)。 这将大大简化您的代码。

 foreach (var property in sourceType.GetProperties( BindingFlags.Public | BindingFlags.Instance)) { var targetProperty = targetType.GetProperty( property.Name, BindingFlags.Public | BindingFlags.Instance ); if (targetProperty != null && targetProperty.CanWrite && targetProperty.PropertyType.IsAssignableFrom(property.PropertyType)) { targetProperty.SetValue( target, property.GetValue(source, null), null ); } } 

C#Reflection IL – 了解如何复制值

问题是关于克隆所以源对象的类型与目标对象的类型相同(虽然据我所知,你可以在源和目标中有不同的类型),但这仍然值得分析。

我写了一篇关于如何做的博客文章(仅限葡萄牙语,但你可以阅读代码)

http://elemarjr.net/2012/02/27/um-helper-para-shallow-cloning-emitting-em-c/

您可以从以下代码获取代码:

https://github.com/ElemarJR/FluentIL/blob/master/demos/Cloning/src/Cloning/Cloning/ILCloner.cs

我认为使用Reflection.Emit比需要的更难。 因此,我编写了一个名为FluentIL(www.fluentil.org)的开源库,以使其更容易。 我在这里用它。

[]中