2012-01-02 17 views
22

Tengo un objeto modelo MyObject con varias propiedades. En un momento, tengo dos instancias de estos MyObject: instancia A e instancia B. Me gustaría copiar y reemplazar las propiedades en la instancia A con las de la instancia B si la instancia B tiene valores no nulos.fusionando dos objetos en C#

Si solo tuviera 1 clase con 3 propiedades, no hay problema, podría codificarla fácilmente (que es lo que comencé a hacer). Pero en realidad tengo 12 modelos de objetos diferentes con aproximadamente 10 propiedades cada uno.

¿Qué es bueno hacer esto?

+4

¿Alguna vez ha intentado algo así como AutoMapper (http://automapper.codeplex.com/)? –

+3

La elección obvia es la reflexión, pero pagará una penalización de rendimiento ... – jondavidjohn

+2

Mire esta respuesta; Creo que cubre su caso: http://stackoverflow.com/questions/571982/iterating-over-class-properties – StilesCrisis

Respuesta

30

Actualización Use AutoMapper en su lugar si necesita invocar este método mucho. Automapper construye métodos dinámicos usando Reflection.Emit y será mucho más rápido que la reflexión. '

Se puede copiar los valores de las propiedades que utilizan la reflexión:

public void CopyValues<T>(T target, T source) 
{ 
    Type t = typeof(T); 

    var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite); 

    foreach (var prop in properties) 
    { 
     var value = prop.GetValue(source, null); 
     if (value != null) 
      prop.SetValue(target, value, null); 
    } 
} 

lo he hecho genérico para garantizar la seguridad de tipos. Si desea incluir propiedades privadas, debe utilizar una anulación de Type.GetProperties(), especificando banderas de enlace.

+0

bien, parece que es lo que necesito. ¿Hay una penalización de rendimiento por usar la reflexión? – frenchie

+1

¿Qué pasa con su requisito de copiar solo propiedades no nulas? –

+0

Sí, la pena de rendimiento es bastante significativa, de acuerdo con este tipo alrededor de 15 veces más lento: http://www.manuelabadia.com/blog/PermaLink,guid,772c7152-b00e-4334-b677-bfbdcd8e6b5d.aspx – Bas

1

Puede hacerlo utilizando la reflexión, pero como alguien ha dicho, tendrá una penalización de rendimiento.

Puesto que usted está trabajando con un diseño de clase esperado, se puede lograr el mismo objetivo mediante el uso de un método de extensión de este modo:

public static class MyClassExtensions 
{ 
    public static void Merge(this MyClass instanceA, MyClass instanceB) 
    { 
     if(instanceA != null && instanceB != null) 
     { 
      if(instanceB.Prop1 != null) 
      { 
       instanceA.Prop1 = instanceB.Prop1; 
      } 


      if(instanceB.PropN != null) 
      { 
       instanceA.PropN = instanceB.PropN; 
      } 
    } 
} 

Y después, en algún lugar de su código:

someInstanceOfMyClass.Merge(someOtherInstanceOfMyClass); 

Al final del día, ha centralizado esta operación en un método de extensión y si agrega o elimina una propiedad de su clase, solo necesita modificar la implementación del método de extensión y obtendrá todo listo.

+0

Esto todavía causará problemas de mantenimiento ya que OP mencionó – Bas

+0

@BasB. No estoy seguro de esto, porque como escribí en mi respuesta, es un diseño predecible y dudo que esta clase agregue o elimine toneladas de propiedades en tiempo extra. Por lo tanto, al final del día, como dije, tiene el mismo efecto que hacerlo con la reflexión, sin su penalización de rendimiento. –

+1

@BasB Además, estos problemas de mantenimiento se pueden resolver utilizando una plantilla T4 en Visual Studio, por lo que eliminar o agregar una propiedad puede desencadenar un proceso de generación de código y volver a crear este método de extensión. Por qué no? WCF y muchos frameworks .NET están resolviendo esto usando la generación de código :) –

6

He intentado Merge Two Objects into an Anonymous Type por Kyle Finley y está funcionando perfecto.

Con la TypeMerger la fusión es tan simple como

var obj1 = new {foo = "foo"}; 

var obj2 = new {bar = "bar"}; 

var mergedObject = TypeMerger.MergeTypes(obj1 , obj2); 

Eso es lo que consiguió el objeto fusionado, aparte de eso, hay una disposición a ignorar las propiedades específicas también. Puedes usar lo mismo para MVC3 también.

+0

ok, gracias por su respuesta – frenchie

+1

nota: el enlace ya no está disponible. – stephenbayer

+1

@stephenbayer el enlace está de vuelta ahora .. –

0

Esto es lo mismo que @Bas respuesta, pero para la fusión de 2 Objeto listas

public class Copycontents 
{ 
    public static void Work<T>(IList<T> targetList, IList<T> sourceList, Func<T, int> selector) 
    { 
     var matchingPrimaryKey = targetList.Select(x => selector(x)).ToList(); 

     foreach (var thismatchingPrimaryKey in matchingPrimaryKey) 
     { 
      CopyValues<T>(targetList.Single(x => selector(x) == thismatchingPrimaryKey), 
       sourceList.Single(x => selector(x) == thismatchingPrimaryKey)); 
     } 
    } 

    private static void CopyValues<T>(T target, T source) 
    { 
     Type t = typeof(T); 

     var properties = t.GetProperties().Where(prop => prop.CanRead && prop.CanWrite); 

     foreach (var prop in properties) 
     { 
      var value = prop.GetValue(source, null); 
      if (value != null) 
       prop.SetValue(target, value, null); 
     } 
    } 
} 
0

He escrito mi propia clase para este propósito: ObjectMerger.

Básicamente utiliza reflejos (y puede ser lento debido a eso). También contiene más funciones, p. analizar objetos para referencias cíclicas y fusionarlos también. Mi ObjectMerger también contiene un mecanismo para manejar clases más complejas como Delegate o MemberInfo. Esos serán copiados por completo, otros objetos en la clase se fusionan recursivamente.

La sintaxis es como:

var initialInstance = new MyObjectBase(); // Initialize first object 
var properInstance = new MyObjectWithAlgorithms(); // Initialize second object 
var result = ObjectMerger.MergeObjects(properInstance, initialInstance); // Merge Objects into type of "properInstance" 

Lo siento decir que no es para uso como tales, debido a que algunas bibliotecas externas no están presentes en el repositorio en el momento debido a las limitaciones en mi compañía, pero pueden ser fácilmente reescritos. Espero que uno pueda agregarlos en el futuro.