2009-07-07 26 views
19

Tengo una clase A y una clase B que hereda la clase A y la amplía con algunos campos más.¿Cómo "clonar" un objeto en un objeto de subclase?

Tener un objeto de tipo aA, ¿cómo puedo crear un objeto de tipo bB que contiene todos los datos que se oponen a contenida?

He intentado a.MemberwiseClone() pero eso solo me da otro tipo de objeto A. Y no puedo convertir A en B ya que la relación de herencia solo permite el lanzamiento opuesto.

¿Cuál es la forma correcta de hacerlo?

+0

Gracias por las respuestas. Estaba buscando una forma automática de hacerlo, pero estás sugiriendo que no existe tal cosa. :( – Vizu

+1

Desafortunadamente, no. Necesitará agregar un constructor o un método de fábrica de algún tipo. –

+0

@Vizu qué método adoptó, también quiero algo similar, por favor ponga aquí si lo tiene –

Respuesta

8

no hay ningún medio de hacer esto automáticamente incorporado a la lengua ...

Una opción es añadir un constructor para la clase B que tiene una clase A como un argumento.

entonces se podría hacer:

B newB = new B(myA); 

El constructor puede simplemente copiar los datos pertinentes a través de, según sea necesario, en ese caso.

+0

es posible copiar una subclase a superclase que tenga algunos de los campos similares –

1

Cree un cursor en B que le permita pasar un objeto de tipo A, luego copie los campos A y establezca los campos B según corresponda.

0

Puede hacer un método Convert en la clase B que tome en la clase base.

public ClassB Convert(ClassA a) 
{ 
    ClassB b = new ClassB(); 
    // Set the properties 
    return b; 
} 

También podría tener un constructor para ClassB take en un objeto de ClassA.

11

Agregaría un constructor de copia a A y luego agregaría un nuevo constructor a B que toma una instancia de A y la pasa al constructor de copia de la base.

+2

La respuesta aceptada es lo que siempre he hecho, pero este giro es simple y elegante. – JMD

0

No, no puedes hacer eso. Una forma de lograr esto es agregar un constructor en la clase B que acepte un parámetro de tipo B y agregar datos manualmente.

Por lo que podría tener algo como esto:

public class B 
{ 
    public B(A a) 
    { 
    this.Foo = a.foo; 
    this.Bar = a.bar; 
    // add some B-specific data here 
    } 
} 
+3

No estoy de acuerdo con que deba tener un método Clone() en A que devuelve una B, ya que esto introduce una dependencia circular. –

+0

Estoy de acuerdo con el constructor para la clase B, pero ¿por qué necesita el CloneToB?() método? –

+0

ehm sí, tienes razón, no debería haber incluido ese método. Lo incluí porque el cartel mencionaba MemberWiseCl uno(). De todos modos, este código no es bueno y lo eliminaré. Gracias. – Razzie

2

Usando Factory Method Pattern:

private abstract class A 
    { 
     public int A1 { get; set; } 

     public abstract A CreateInstance(); 

     public virtual A Clone() 
     { 
      var instance = CreateInstance(); 
      instance.A1 = A1; 
      return instance; 
     } 
    } 

    private class B : A 
    { 
     public int A3 { get; set; } 

     public override A CreateInstance() 
     { 
      return new B(); 
     } 

     public override A Clone() 
     { 
      var result = (B) base.Clone(); 
      result.A3 = A3; 
      return result; 
     } 
    } 

    private static void Main(string[] args) 
    { 
     var b = new B() { A1 = 1, A3 = 2 }; 

     var c = b.Clone(); 
    } 
3

Esto se puede conseguir mediante el uso de la reflexión.

Ventaja: Maintainable. No es necesario cambiar el copy-constructor o similar, agregar o eliminar propiedades.

Desventaja: Rendimiento. La reflexión es lenta. Sin embargo, todavía estamos hablando de milisegundos en clases de tamaño medio.

Aquí hay una copia superficial aplicación basada en la reflexión apoyo copia a subclase, utilizando métodos de extensión:

public static TOut GetShallowCopyByReflection<TOut>(this Object objIn) 
{ 
    Type inputType = objIn.GetType(); 
    Type outputType = typeof(TOut); 
    if (!outputType.Equals(inputType) && !outputType.IsSubclassOf(inputType)) throw new ArgumentException(String.Format("{0} is not a sublcass of {1}", outputType, inputType)); 
    PropertyInfo[] properties = inputType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); 
    FieldInfo[] fields = inputType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy); 
    TOut objOut = (TOut)Activator.CreateInstance(typeof(TOut)); 
    foreach (PropertyInfo property in properties) 
    { 
     try 
     { 
      property.SetValue(objIn, property.GetValue(objIn, null), null); 
     } 
     catch (ArgumentException) { } // For Get-only-properties 
    } 
    foreach (FieldInfo field in fields) 
    { 
     field.SetValue(objOut, field.GetValue(objIn)); 
    } 
    return objOut; 
} 

Este método copiará todas las propiedades - privado y público, así como todos los campos. Las propiedades se copian por referencia, lo que la convierte en una copia superficial.

Las pruebas unitarias:

[TestClass] 
public class ExtensionTests { 
    [TestMethod] 
    public void GetShallowCloneByReflection_PropsAndFields() 
    { 
     var uri = new Uri("http://www.stackoverflow.com"); 
     var source = new TestClassParent(); 
     source.SomePublicString = "Pu"; 
     source.SomePrivateString = "Pr"; 
     source.SomeInternalString = "I"; 
     source.SomeIntField = 6; 
     source.SomeList = new List<Uri>() { uri }; 

     var dest = source.GetShallowCopyByReflection<TestClassChild>(); 
     Assert.AreEqual("Pu", dest.SomePublicString); 
     Assert.AreEqual("Pr", dest.SomePrivateString); 
     Assert.AreEqual("I", dest.SomeInternalString); 
     Assert.AreEqual(6, dest.SomeIntField); 
     Assert.AreSame(source.SomeList, dest.SomeList); 
     Assert.AreSame(uri, dest.SomeList[0]);    
    } 
} 

internal class TestClassParent 
{ 
    public String SomePublicString { get; set; } 
    internal String SomeInternalString { get; set; } 
    internal String SomePrivateString { get; set; } 
    public String SomeGetOnlyString { get { return "Get"; } } 
    internal List<Uri> SomeList { get; set; } 
    internal int SomeIntField; 
} 

internal class TestClassChild : TestClassParent {} 
+0

¿Qué cuenta como tamaño de clase promedio? –

+0

@ adam-l-s Hehe buena pregunta. Mi respuesta como siempre en lo que respecta al rendimiento: Medida. Si es lo suficientemente rápido para ti, úsalo. Se dice que la reflexión es 1000 veces más lenta que el acceso a las propiedades de la manera normal: http://stackoverflow.com/questions/25458/how-costly-is-net-reflection – Nilzor

0

En su clase base añadir el método virtual CreateObject a continuación ...

public virtual T CreateObject<T>() 
    { 
     if (typeof(T).IsSubclassOf(this.GetType())) 
     { 
      throw new InvalidCastException(this.GetType().ToString() + " does not inherit from " + typeof(T).ToString()); 
     } 

     T ret = System.Activator.CreateInstance<T>(); 

     PropertyInfo[] propTo = ret.GetType().GetProperties(); 
     PropertyInfo[] propFrom = this.GetType().GetProperties(); 

     // for each property check whether this data item has an equivalent property 
     // and copy over the property values as neccesary. 
     foreach (PropertyInfo propT in propTo) 
     { 
      foreach (PropertyInfo propF in propFrom) 
      { 
       if (propT.Name == propF.Name) 
       { 
        propF.SetValue(ret,propF.GetValue(this)); 
        break; 
       } 
      } 
     } 

     return ret; 
    } 

luego decir que desea crear una subclase de objetos de la vida real de la superclase solo llame al

this.CreateObject<subclass>(); 

¡Eso debería hacerlo!

0

Si bien nadie sugirió esto (y esto no funcionará para todos, ciertamente), se debe decir que si tiene la opción de crear el objeto b desde el principio, haga eso en lugar de crear el objeto a entonces copiando al objeto b. Por ejemplo, imagine que está en la misma función y tienen este código:

var a = new A(); 
a.prop1 = "value"; 
a.prop2 = "value"; 
... 
// now you need a B object instance... 
var b = new B(); 
// now you need to copy a into b... 

lugar de preocuparse por el último paso comentado, a empezar con B y ajuste los valores:

var b = new B(); 
b.prop1 = "value"; 
b.prop2 = "value"; 

ponen por favor ¡No me votaste porque crees que lo de arriba es estúpido! Me he encontrado con muchos programadores que están tan concentrados en su código que no se dieron cuenta de que una solución más simple es mirarlos a la cara. :)

Cuestiones relacionadas