2010-09-08 19 views
20

mi pregunta se muestra en el códigoclase fundido a otra clase o convertir clase a otra

que tienen clase como que

public class maincs 
{ 
    public int a; 
    public int b; 
    public int c; 
    public int d; 
} 

public class sub1 
{ 
    public int a; 
    public int b; 
    public int c; 
} 


public void methoda (sub1 model) 
{ 
    maincs mdata = new maincs(){a = model.a , b = model.b , c= model.c} ; 

    // is there is a way to directly cast class sub1 into main like that  
    mdata = (maincs) model;  
} 
+1

Este código no tiene sentido. Código postal que compila (no es el reparto, ese es el problema). –

+7

Bastante duro ... Perder una palabra clave 'clase' no quita el sentido. – nawfal

+0

Ya existen bibliotecas de mapeadores ligeras ya escritas para este propósito. Manejan mucho más casos extremos. Puedes googlearlo – nawfal

Respuesta

23

Lo que quiere decir es:

"Si tiene dos clases que comparten la mayor parte de las mismas propiedades que puede convertir un objeto de la clase a a clase b y automáticamente hacer que el sistema comprender la asignación a través de la compartida nombres de propiedad? "

Opción 1: Uso de reflexión

Desventaja: Se te va lento hacia abajo más de lo que cree.

Opción 2: Haz que una clase se derive de otra, la primera con propiedades comunes y otra con una extensión de eso.

Desventaja: ¡acoplado! ¡Si estás haciendo eso para dos capas en tu aplicación, entonces las dos capas estarán acopladas!

Pero no hay que:

class customer 
{ 
    public string firstname { get; set; } 
    public string lastname { get; set; } 
    public int age { get; set; } 
} 
class employee 
{ 
    public string firstname { get; set; } 
    public int age { get; set; } 
} 

Ahora aquí es una extensión para el tipo de objeto:

public static T Cast<T>(this Object myobj) 
{ 
    Type objectType = myobj.GetType(); 
    Type target = typeof(T); 
    var x = Activator.CreateInstance(target, false); 
    var z = from source in objectType.GetMembers().ToList() 
     where source.MemberType == MemberTypes.Property select source ; 
    var d = from source in target.GetMembers().ToList() 
     where source.MemberType == MemberTypes.Property select source; 
    List<MemberInfo> members = d.Where(memberInfo => d.Select(c => c.Name) 
     .ToList().Contains(memberInfo.Name)).ToList(); 
    PropertyInfo propertyInfo; 
    object value; 
    foreach (var memberInfo in members) 
    { 
     propertyInfo = typeof(T).GetProperty(memberInfo.Name); 
     value = myobj.GetType().GetProperty(memberInfo.Name).GetValue(myobj,null); 

     propertyInfo.SetValue(x,value,null); 
    } 
    return (T)x; 
} 

Ahora que lo utilice como esto:

static void Main(string[] args) 
{ 
    var cus = new customer(); 
    cus.firstname = "John"; 
    cus.age = 3; 
    employee emp = cus.Cast<employee>(); 
} 

Método elenco cheques propiedades comunes entre dos objetos y hace la asignación automáticamente.

+1

Eso es exactamente –

+0

Buena solución, pero como dijiste, sobrecarga y complejidad :) – Noctis

+0

Supongo que te has perdido para hacer uso de la variable 'z'. Se debe utilizar al inicializar 'miembros' var ie Lista members = z.Where (memberInfo => d.Select (c => c.Name) .ToList(). Contiene (memberInfo.Name)). ToList() ; – Aamol

5

Se podría cambiar la estructura de clases a:

public class maincs : sub1 
{ 
    public int d; 
} 

public class sub1 
{ 
    public int a; 
    public int b; 
    public int c; 
} 

Luego puedes mantener una lista de sub1 y enviar algunos de ellos a mainc.

+0

Esto tampoco compila. Tal vez olvidaste la palabra clave 'class' –

+0

Vaya, eso es lo que obtengo para copiar/pegar. –

43

Ya ha definido la conversión, solo tiene que ir un paso más allá si desea poder transmitir. Por ejemplo:

public class sub1 
{ 
    public int a; 
    public int b; 
    public int c; 

    public static explicit operator maincs(sub1 obj) 
    { 
     maincs output = new maincs() { a = obj.a, b = obj.b, c = obj.c }; 
     return output; 
    } 
} 

de forma que puede hacer algo como

static void Main() 
{ 
    sub1 mySub = new sub1(); 
    maincs myMain = (maincs)mySub; 
} 
+2

Incluso mejor que la respuesta seleccionada – Noctis

+1

Great answer. Gracias. – Ellis

+0

¿Hay alguna implicación para convertir/transmitir de una clase a otra con los puntales exactos usando su código? – Code

2

Puede proporcionar una sobrecarga explícita para el operador de conversión:

public static explicit operator maincs(sub1 val) 
{ 
    var ret = new maincs() { a = val.a, b = val.b, c = val.c }; 
    return ret; 
} 

Otra opción sería utilizar una interfaz que tiene las propiedades a, b y c e implementa la interfaz en ambas clases. Entonces solo tiene que el tipo de parámetro para metoda ser la interfaz en lugar de la clase.

1

Al usar el siguiente código, puede copiar cualquier objeto de clase a otro objeto de clase para el mismo nombre y el mismo tipo de propiedades.

public class CopyClass 
{ 
    /// <summary> 
    /// Copy an object to destination object, only matching fields will be copied 
    /// </summary> 
    /// <typeparam name="T"></typeparam> 
    /// <param name="sourceObject">An object with matching fields of the destination object</param> 
    /// <param name="destObject">Destination object, must already be created</param> 
    public static void CopyObject<T>(object sourceObject, ref T destObject) 
    { 
     // If either the source, or destination is null, return 
     if (sourceObject == null || destObject == null) 
      return; 

     // Get the type of each object 
     Type sourceType = sourceObject.GetType(); 
     Type targetType = destObject.GetType(); 

     // Loop through the source properties 
     foreach (PropertyInfo p in sourceType.GetProperties()) 
     { 
      // Get the matching property in the destination object 
      PropertyInfo targetObj = targetType.GetProperty(p.Name); 
      // If there is none, skip 
      if (targetObj == null) 
       continue; 

      // Set the value in the destination 
      targetObj.SetValue(destObject, p.GetValue(sourceObject, null), null); 
     } 
    } 
} 

Método llamada Al igual que,

ClassA objA = new ClassA(); 
ClassB objB = new ClassB(); 

CopyClass.CopyObject(objOfferMast, ref objB); 

Copiará objA en objB.

+1

Tenga en cuenta que si está utilizando esta solución, es posible que tenga problemas cuando las clases tengan el mismo nombre para las propiedades, pero con diferentes tipos. Por ejemplo: 'public class A {public int Age {get; set;}}' y 'public class B {public string Age {get; set;}}' Lanzará una excepción, si está tratando de convertir de 'A' a' B' – Noctis

+0

Este tipo de conversión también podría causar problemas de rendimiento. Úselo con cuidado y ciertamente no en for-loops – RPDeshaies

11

Otra opción es utilizar la serialización JSON y deserialización:

using Newtonsoft.Json; 

Class1 obj1 = new Class1(); 
Class2 obj2 = JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(obj1)); 

O:

public class Class1 
{ 
    public static explicit operator Class2(Class1 obj) 
    { 
     return JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(obj)); 
    } 
} 

de forma que puede hacer algo como

static void Main() 
{ 
    Class1 obj1 = new Class1(); 
    Class2 obj2 = (Class2)obj1; 
} 
+1

Sí, el uso del serializador Newtonsoft json es bastante simple y eficiente. Hay .Net serializer pero encontré que Newtonsoft funciona bien con .Net json serializer. Encontré este enlace que ofrece una breve comparación http://www.newtonsoft.com/json/help/html/JsonNetVsDotNetSerializers.htm – Aamol

1

Hay algunas buenas respuestas aquí, solo quería para agregar un poco de verificación de tipos aquí ya que no podemos suponer que si las propiedades existen con el mismo nombre, que son del mismo tipo. Aquí está mi oferta, que se extiende a la respuesta anterior, muy excelente, ya que tuve algunos pequeños problemas técnicos con ella.

En esta versión he permitido que el consumidor especifique los campos que se excluirán, y también de forma predeterminada para excluir cualquier base de datos/propiedades relacionadas específicas del modelo.

public static T Transform<T>(this object myobj, string excludeFields = null) 
    { 
     // Compose a list of unwanted members 
     if (string.IsNullOrWhiteSpace(excludeFields)) 
      excludeFields = string.Empty; 
     excludeFields = !string.IsNullOrEmpty(excludeFields) ? excludeFields + "," : excludeFields; 
     excludeFields += $"{nameof(DBTable.ID)},{nameof(DBTable.InstanceID)},{nameof(AuditableBase.CreatedBy)},{nameof(AuditableBase.CreatedByID)},{nameof(AuditableBase.CreatedOn)}"; 

     var objectType = myobj.GetType(); 
     var targetType = typeof(T); 
     var targetInstance = Activator.CreateInstance(targetType, false); 

     // Find common members by name 
     var sourceMembers = from source in objectType.GetMembers().ToList() 
            where source.MemberType == MemberTypes.Property 
            select source; 
     var targetMembers = from source in targetType.GetMembers().ToList() 
            where source.MemberType == MemberTypes.Property 
            select source; 
     var commonMembers = targetMembers.Where(memberInfo => sourceMembers.Select(c => c.Name) 
      .ToList().Contains(memberInfo.Name)).ToList(); 

     // Remove unwanted members 
     commonMembers.RemoveWhere(x => x.Name.InList(excludeFields)); 

     foreach (var memberInfo in commonMembers) 
     { 
      if (!((PropertyInfo)memberInfo).CanWrite) continue; 

      var targetProperty = typeof(T).GetProperty(memberInfo.Name); 
      if (targetProperty == null) continue; 

      var sourceProperty = myobj.GetType().GetProperty(memberInfo.Name); 
      if (sourceProperty == null) continue; 

      // Check source and target types are the same 
      if (sourceProperty.PropertyType.Name != targetProperty.PropertyType.Name) continue; 

      var value = myobj.GetType().GetProperty(memberInfo.Name)?.GetValue(myobj, null); 
      if (value == null) continue; 

      // Set the value 
      targetProperty.SetValue(targetInstance, value, null); 
     } 
     return (T)targetInstance; 
    } 
Cuestiones relacionadas