将属性值从一个对象自动应用到另一个相同的types?
给定2个types为T的对象A和B,我想在A中将属性的值赋给B中的相同属性,而不对每个属性做明确的赋值。
我想保存这样的代码:
b.Nombre = a.Nombre; b.Descripcion = a.Descripcion; b.Imagen = a.Imagen; b.Activo = a.Activo;
做一些像
a.ApplyProperties(b);
可能吗?
我有一个名为PropertyCopy
MiscUtil
types,虽然它创build了一个新的目标types的实例,并将属性复制到其中。
它不需要types相同 – 它只是将所有可读属性从“源”types复制到“目标”types。 当然,如果types是相同的,那更有可能工作:)这是一个浅拷贝,顺便说一句。
在这个答案底部的代码块中,我已经扩展了这个类的function。 要从一个实例复制到另一个实例,它在执行时使用简单的PropertyInfo
值 – 这比使用expression式树要慢,但另一种方法是编写一个dynamic方法,我不太热。 如果performance对你来说绝对至关重要,让我知道,我会看看我能做些什么。 要使用该方法,请写下如下内容:
MyType instance1 = new MyType(); // Do stuff MyType instance2 = new MyType(); // Do stuff PropertyCopy.Copy(instance1, instance2);
(其中Copy
是使用types推断调用的通用方法)。
我并没有准备好做一个完整的MiscUtil版本,但是这是更新的代码,包括注释。 我不打算为SO编辑器重新打包它们 – 只是复制整个块。
(如果我是从头开始的话,我也可能会重新deviseAPI的命名方面,但我不想打破现有的用户…)
#if DOTNET35 using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; namespace MiscUtil.Reflection { /// <summary> /// Non-generic class allowing properties to be copied from one instance /// to another existing instance of a potentially different type. /// </summary> public static class PropertyCopy { /// <summary> /// Copies all public, readable properties from the source object to the /// target. The target type does not have to have a parameterless constructor, /// as no new instance needs to be created. /// </summary> /// <remarks>Only the properties of the source and target types themselves /// are taken into account, regardless of the actual types of the arguments.</remarks> /// <typeparam name="TSource">Type of the source</typeparam> /// <typeparam name="TTarget">Type of the target</typeparam> /// <param name="source">Source to copy properties from</param> /// <param name="target">Target to copy properties to</param> public static void Copy<TSource, TTarget>(TSource source, TTarget target) where TSource : class where TTarget : class { PropertyCopier<TSource, TTarget>.Copy(source, target); } } /// <summary> /// Generic class which copies to its target type from a source /// type specified in the Copy method. The types are specified /// separately to take advantage of type inference on generic /// method arguments. /// </summary> public static class PropertyCopy<TTarget> where TTarget : class, new() { /// <summary> /// Copies all readable properties from the source to a new instance /// of TTarget. /// </summary> public static TTarget CopyFrom<TSource>(TSource source) where TSource : class { return PropertyCopier<TSource, TTarget>.Copy(source); } } /// <summary> /// Static class to efficiently store the compiled delegate which can /// do the copying. We need a bit of work to ensure that exceptions are /// appropriately propagated, as the exception is generated at type initialization /// time, but we wish it to be thrown as an ArgumentException. /// Note that this type we do not have a constructor constraint on TTarget, because /// we only use the constructor when we use the form which creates a new instance. /// </summary> internal static class PropertyCopier<TSource, TTarget> { /// <summary> /// Delegate to create a new instance of the target type given an instance of the /// source type. This is a single delegate from an expression tree. /// </summary> private static readonly Func<TSource, TTarget> creator; /// <summary> /// List of properties to grab values from. The corresponding targetProperties /// list contains the same properties in the target type. Unfortunately we can't /// use expression trees to do this, because we basically need a sequence of statements. /// We could build a DynamicMethod, but that's significantly more work :) Please mail /// me if you really need this... /// </summary> private static readonly List<PropertyInfo> sourceProperties = new List<PropertyInfo>(); private static readonly List<PropertyInfo> targetProperties = new List<PropertyInfo>(); private static readonly Exception initializationException; internal static TTarget Copy(TSource source) { if (initializationException != null) { throw initializationException; } if (source == null) { throw new ArgumentNullException("source"); } return creator(source); } internal static void Copy(TSource source, TTarget target) { if (initializationException != null) { throw initializationException; } if (source == null) { throw new ArgumentNullException("source"); } for (int i = 0; i < sourceProperties.Count; i++) { targetProperties[i].SetValue(target, sourceProperties[i].GetValue(source, null), null); } } static PropertyCopier() { try { creator = BuildCreator(); initializationException = null; } catch (Exception e) { creator = null; initializationException = e; } } private static Func<TSource, TTarget> BuildCreator() { ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source"); var bindings = new List<MemberBinding>(); foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance)) { if (!sourceProperty.CanRead) { continue; } PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name); if (targetProperty == null) { throw new ArgumentException("Property " + sourceProperty.Name + " is not present and accessible in " + typeof(TTarget).FullName); } if (!targetProperty.CanWrite) { throw new ArgumentException("Property " + sourceProperty.Name + " is not writable in " + typeof(TTarget).FullName); } if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0) { throw new ArgumentException("Property " + sourceProperty.Name + " is static in " + typeof(TTarget).FullName); } if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType)) { throw new ArgumentException("Property " + sourceProperty.Name + " has an incompatible type in " + typeof(TTarget).FullName); } bindings.Add(Expression.Bind(targetProperty, Expression.Property(sourceParameter, sourceProperty))); sourceProperties.Add(sourceProperty); targetProperties.Add(targetProperty); } Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings); return Expression.Lambda<Func<TSource, TTarget>>(initializer, sourceParameter).Compile(); } } } #endif
因为我相信Jon的版本太复杂了,Steve的版本太简单了,我喜欢Daniel的扩展类的想法。
另外一个通用版本是相当不必要的,因为所有的项目都是对象。
我想志愿我的精益和平均的版本。 所有上述的积分。 :d
码:
using System; using System.Reflection; /// <summary> /// A static class for reflection type functions /// </summary> public static class Reflection { /// <summary> /// Extension for 'Object' that copies the properties to a destination object. /// </summary> /// <param name="source">The source.</param> /// <param name="destination">The destination.</param> public static void CopyProperties(this object source, object destination) { // If any this null throw an exception if (source == null || destination == null) throw new Exception("Source or/and Destination Objects are null"); // Getting the Types of the objects Type typeDest = destination.GetType(); Type typeSrc = source.GetType(); // Iterate the Properties of the source instance and // populate them from their desination counterparts PropertyInfo[] srcProps = typeSrc.GetProperties(); foreach (PropertyInfo srcProp in srcProps) { if (!srcProp.CanRead) { continue; } PropertyInfo targetProperty = typeDest.GetProperty(srcProp.Name); if (targetProperty == null) { continue; } if (!targetProperty.CanWrite) { continue; } if (targetProperty.GetSetMethod(true) != null && targetProperty.GetSetMethod(true).IsPrivate) { continue; } if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0) { continue; } if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)) { continue; } // Passed all tests, lets set the value targetProperty.SetValue(destination, srcProp.GetValue(source, null), null); } } }
用法:
/// <summary> /// ExampleCopyObject /// </summary> /// <returns></returns> public object ExampleCopyObject() { object destObject = new object(); this.CopyProperties(destObject); // inside a class you want to copy from Reflection.CopyProperties(this, destObject); // Same as above but directly calling the function TestClass srcClass = new TestClass(); TestStruct destStruct = new TestStruct(); srcClass.CopyProperties(destStruct); // using the extension directly on a object Reflection.CopyProperties(srcClass, destObject); // Same as above but directly calling the function //so on and so forth.... your imagination is the limits :D return srcClass; } public class TestClass { public string Blah { get; set; } } public struct TestStruct { public string Blah { get; set; } }
当我感到无聊,一个linq版本被build议的评论
using System; using System.Linq; using System.Reflection; /// <summary> /// A static class for reflection type functions /// </summary> public static class Reflection { /// <summary> /// Extension for 'Object' that copies the properties to a destination object. /// </summary> /// <param name="source">The source.</param> /// <param name="destination">The destination.</param> public static void CopyProperties(this object source, object destination) { // If any this null throw an exception if (source == null || destination == null) throw new Exception("Source or/and Destination Objects are null"); // Getting the Types of the objects Type typeDest = destination.GetType(); Type typeSrc = source.GetType(); // Collect all the valid properties to map var results = from srcProp in typeSrc.GetProperties() let targetProperty = typeDest.GetProperty(srcProp.Name) where srcProp.CanRead && targetProperty != null && (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate) && (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0 && targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType) select new { sourceProperty = srcProp, targetProperty = targetProperty }; //map the properties foreach (var props in results) { props.targetProperty.SetValue(destination, props.sourceProperty.GetValue(source, null), null); } } }
基于Steve的方法,我采用了扩展方法。 这使用我的基类作为types,但应使用对象作为参数types。 非常适合我的用途。
using System.Reflection; //*Namespace Here* public static class Ext { public static void CopyProperties(this EntityBase source, EntityBase destination) { // Iterate the Properties of the destination instance and // populate them from their source counterparts PropertyInfo[] destinationProperties = destination.GetType().GetProperties(); foreach (PropertyInfo destinationPi in destinationProperties) { PropertyInfo sourcePi = source.GetType().GetProperty(destinationPi.Name); destinationPi.SetValue(destination, sourcePi.GetValue(source, null), null); } } }
用法如下所示:
item1.CopyProperties(item2);
现在Item2具有与item1相同的属性数据。
这里有一个简短而又甜美的版本,因为你说过你的两个对象是同一个types的:
foreach (PropertyInfo property in typeof(YourType).GetProperties()) { property.SetValue(targetObject, property.GetValue(sourceObject, null), null); }
有ICloneable和object.MemberwiseClone (浅拷贝)(这些创build一个全新的对象,所以可能不符合你的要求)。
你可以使用reflection来自己做(从一个基类inheritance,所以你不必重新实现)。
或者你可以代码生成它。
你可以尝试这样的事情….
MyType destination = new MyType(); MyType source = new MyType(); // Iterate the Properties of the destination instance and // populate them from their source counterparts PropertyInfo[] destinationProperties = destination.GetType().GetProperties(); foreach (PropertyInfo destinationPI in destinationProperties) { PropertyInfo sourcePI = source.GetType().GetProperty(destinationPI.Name); destinationPI.SetValue(destination, sourcePI.GetValue(source, null), null); }
如果你想要像ApplyProperties这样的东西,你可以在Object上写一个扩展方法来做你所需要的。 只要认识到这样的扩展方法不会是“纯”的,或者是副作用的。 但是,如果你需要这个能力,那么就有办法完成它。
您可以使用序列化来深入克隆对象:
public static T DeepClone<T>(this T objectToClone) where T: BaseClass { BinaryFormatter bFormatter = new BinaryFormatter(); MemoryStream stream = new MemoryStream(); bFormatter.Serialize(stream, objectToClone); stream.Seek(0, SeekOrigin.Begin); T clonedObject = (T)bFormatter.Deserialize(stream); return clonedObject; }
当然,类只需要标记Serializable。
public static void GatherFrom<TSelf, TSource>(this TSelf self, TSource source) { PropertyInfo[] sourceAllProperties = source.GetType().GetProperties(); foreach (PropertyInfo sourceProperty in sourceAllProperties) { PropertyInfo selfProperty = self.GetType().GetProperty(sourceProperty.Name); if (selfProperty != null) { var sourceValue = sourceProperty.GetValue(source, null); selfProperty.SetValue(self, sourceValue, null); } } }
丹尼尔版本的修改,以避免例外。
foreach (PropertyInfo property in typeof(YourType).GetProperties()) { if (property.CanWrite) { property.SetValue(marketData, property.GetValue(market, null), null); } }