¿Aplica valores de propiedades de un objeto a otro del mismo tipo automáticamente?

Dado 2 objetos A y B de tipo T, quiero asignar los valores de las propiedades en A a las mismas propiedades en B sin hacer una asignación explícita para cada propiedad.

Quiero guardar código así:

b.Nombre = a.Nombre; b.Descripcion = a.Descripcion; b.Imagen = a.Imagen; b.Activo = a.Activo; 

haciendo algo como

 a.ApplyProperties(b); 

¿Es posible?

Tengo un tipo en MiscUtil llamado PropertyCopy que hace algo similar, aunque crea una nueva instancia del tipo de destino y copia las propiedades en eso.

No requiere que los tipos sean iguales, simplemente copia todas las propiedades legibles del tipo “fuente” al tipo “objective”. Por supuesto, si los tipos son los mismos, es más probable que funcione 🙂 Es una copia superficial, por cierto.

En el bloque de código al final de esta respuesta, amplié las capacidades de la clase. Para copiar de una instancia a otra, utiliza valores PropertyInfo simples en el momento de la ejecución, esto es más lento que usar un árbol de expresiones, pero la alternativa sería escribir un método dynamic, que no estoy demasiado caliente. Si el rendimiento es absolutamente crítico para usted, avíseme y veré lo que puedo hacer. Para usar el método, escriba algo como:

 MyType instance1 = new MyType(); // Do stuff MyType instance2 = new MyType(); // Do stuff PropertyCopy.Copy(instance1, instance2); 

(donde Copy es un método genérico llamado usando inferencia de tipo).

No estoy realmente listo para hacer una versión completa de MiscUtil, pero aquí está el código actualizado, incluidos los comentarios. No voy a volver a envolverlos para el editor de SO, solo copie todo el fragmento.

(Probablemente también rediseñé un poco la API en términos de nombres si comenzara de cero, pero no quiero romper los usuarios existentes …)

 #if DOTNET35 using System; using System.Collections.Generic; using System.Linq.Expressions; using System.Reflection; namespace MiscUtil.Reflection { ///  /// Non-generic class allowing properties to be copied from one instance /// to another existing instance of a potentially different type. ///  public static class PropertyCopy { ///  /// 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. ///  /// Only the properties of the source and target types themselves /// are taken into account, regardless of the actual types of the arguments. /// Type of the source /// Type of the target /// Source to copy properties from /// Target to copy properties to public static void Copy(TSource source, TTarget target) where TSource : class where TTarget : class { PropertyCopier.Copy(source, target); } } ///  /// 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. ///  public static class PropertyCopy where TTarget : class, new() { ///  /// Copies all readable properties from the source to a new instance /// of TTarget. ///  public static TTarget CopyFrom(TSource source) where TSource : class { return PropertyCopier.Copy(source); } } ///  /// 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. ///  internal static class PropertyCopier { ///  /// 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. ///  private static readonly Func creator; ///  /// 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... ///  private static readonly List sourceProperties = new List(); private static readonly List targetProperties = new List(); 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 BuildCreator() { ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source"); var bindings = new List(); 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>(initializer, sourceParameter).Compile(); } } } #endif 

Porque creo que la versión de Jon es un tanto complicada y la versión de Steve es demasiado simple, y me gusta la idea de Daniel de una clase de extensión.

Además, una versión genérica es bonita pero innecesaria ya que todos los elementos son objetos.

Me gustaría ofrecer mi versión magra y mezquina. Créditos a todo lo anterior. :RE

Código:

 using System; using System.Reflection; ///  /// A static class for reflection type functions ///  public static class Reflection { ///  /// Extension for 'Object' that copies the properties to a destination object. ///  /// The source. /// The destination. 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); } } } 

Uso:

 ///  /// ExampleCopyObject ///  ///  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; } } 

Como estaba aburrido y una versión de linq fue sugerida por un comentario

 using System; using System.Linq; using System.Reflection; ///  /// A static class for reflection type functions ///  public static class Reflection { ///  /// Extension for 'Object' that copies the properties to a destination object. ///  /// The source. /// The destination. 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); } } } 

Aprovechando el método de Steve, opté por el método de extensión. Esto usa mi clase base como tipo, pero debería ser utilizable incluso usando object como los tipos param. Funciona muy bien para mis usos.

 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); } } } 

El uso se ve así:

 item1.CopyProperties(item2); 

Ahora Item2 tiene los mismos datos de propiedad que item1.

Aquí hay una versión corta y dulce, ya que dijiste que tus dos objetos son del mismo tipo:

 foreach (PropertyInfo property in typeof(YourType).GetProperties()) { property.SetValue(targetObject, property.GetValue(sourceObject, null), null); } 

Modificación de la versión de Daniel para evitar excepciones.

 foreach (PropertyInfo property in typeof(YourType).GetProperties()) { if (property.CanWrite) { property.SetValue(marketData, property.GetValue(market, null), null); } } 

Hay ICloneable y object.MemberwiseClone (copia superficial) (estos crean un objeto completamente nuevo, por lo que pueden no cumplir con sus requisitos).

Podría usar el reflection para hacerlo usted mismo (heredar de una clase base para que no tenga que volver a implementarlo).

O podrías codificarlo generando.

Puede intentar algo como esto …

 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); } 

Puede usar la serialización para clonar profundamente el objeto:

 public static T DeepClone(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; } 

Las clases solo tendrían que marcarse Serializable por supuesto.

Si quiere algo como ApplyProperties, podría escribir un método de extensión en Object que haría lo que necesita. Solo tenga en cuenta que dicho método de extensión no sería “puro” ni libre de efectos secundarios. Pero si necesita la capacidad, es una forma de lograrlo.