Applica automaticamente i valori delle proprietà da un object a un altro dello stesso tipo?

Dati 2 oggetti A e B di tipo T, voglio assegnare i valori delle proprietà in A alle stesse proprietà in B senza eseguire un’assegnazione esplicita per ogni proprietà.

Voglio salvare il codice in questo modo:

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

fare qualcosa del genere

 a.ApplyProperties(b); 

È ansible?

Ho un tipo in MiscUtil chiamato PropertyCopy che fa qualcosa di simile – anche se crea una nuova istanza del tipo di destinazione e copia le proprietà in quello.

Non richiede che i tipi siano uguali: copia solo tutte le proprietà leggibili dal tipo “source” al tipo “target”. Ovviamente se i tipi sono uguali, è più probabile che funzionino 🙂 È una copia poco profonda, btw.

Nel blocco di codice in fondo a questa risposta, ho esteso le capacità della class. Per copiare da un’istanza a un’altra, utilizza semplici valori PropertyInfo al momento dell’esecuzione – questo è più lento rispetto all’utilizzo di un albero di espressioni, ma l’alternativa sarebbe scrivere un metodo dinamico, su cui non sono troppo caldo. Se le prestazioni sono assolutamente critiche per te, fammi sapere e vedrò cosa posso fare. Per usare il metodo, scrivi qualcosa come:

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

(dove Copy è un metodo generico chiamato usando inferenza di tipo).

Non sono davvero pronto per fare una versione completa di MiscUtil, ma ecco il codice aggiornato, inclusi i commenti. Non ho intenzione di riavvolgerli per l’editor SO – basta copiare l’intero blocco.

(Probabilmente mi piacerebbe anche ridisegnare l’API un po ‘in termini di nomi se stavo partendo da zero, ma non voglio rompere gli utenti esistenti …)

 #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 

Perché credo che la versione di Jon sia un po ‘troppo complicata e la versione di Steve è troppo semplice, e mi piace l’idea di Daniel di una class di estensione.

Inoltre una versione generica è carina ma non necessaria in quanto tutti gli oggetti sono oggetti.

Mi piacerebbe offrire volontariamente la mia versione snella e ctriggers. Crediti a tutto quanto sopra. : D

Codice:

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

Come ero annoiato e una versione di linq è stata suggerita da un commento

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

Partendo dal metodo di Steve, sono andato con l’approccio del metodo di estensione. Questo usa la mia class base come tipo, ma dovrebbe essere utilizzabile anche usando l’object come tipo param. Funziona benissimo per i miei usi.

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

L’utilizzo è simile a questo:

 item1.CopyProperties(item2); 

Ora l’elemento 2 ha gli stessi dati di proprietà di item1.

Ecco una versione breve e dolce, dal momento che hai detto che entrambi i tuoi oggetti sono dello stesso tipo:

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

Modifica della versione di Daniel per evitare eccezioni.

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

C’è ICloneable e object.MemberwiseClone (copia superficiale) (questi creano un object completamente nuovo, quindi potrebbero non soddisfare le tue esigenze).

È ansible utilizzare il reflection per farlo da soli (ereditato da una class base in modo da non doverlo implementare nuovamente).

O potresti codice generarlo.

Potresti provare qualcosa come questo ….

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

È ansible utilizzare la serializzazione per clonare in profondità l’object:

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

Le classi dovrebbero semplicemente essere contrassegnate con Serializable, ovviamente.

Se desideri qualcosa come ApplyProperties, potresti scrivere un metodo di estensione su Object che farebbe ciò di cui hai bisogno. Basta rendersi conto che un tale metodo di estensione non sarebbe “puro”, o privo di effetti collaterali. Ma se hai bisogno della capacità, è un modo per realizzarlo.