2011-12-25 15 views
14

I tienen una clase baseC# inheritance. clase derivada de la clase Base

public class A 
{ 
    public string s1; 
    public string s2; 
} 

También tengo una clase derivada:

public class B : A 
{ 
    public string s3; 
} 

Supongamos mi programa crea una instancia de la clase A.

A aClassInstance = new A(); 

se establecieron algunos parámetros:

aClassInstance.s1 = "string 1"; 
aClassInstance.s2 = "string 2"; 

En este punto me gustaría crear una instancia de la clase B. Pero me gustaría B ya se disponen de valores de mi instancia de la clase A.

esto no funcionó:

public B bClassInstance = new B(): 
bClassInstance = (B)aClassInstance; 

Ni éste:

Hecho un método clon de la clase A.

public B cloneA() {  
    A a = new A(); 
    a = (A)this.MemberwiseClone() 
    return(B)a; 
} 

el código VS toma tanto de lo anterior - bu t tengo errores de tiempo de ejecución

favor ayude

+2

Tenga cuidado al clonar, en particular si su clase tiene campos de tipos de referencia mutables. Decide si quieres un clon profundo o un clon superficial, y documentalo. – TrueWill

+1

derecha. Esta clase en particular no tiene referencias, por lo que un clon superficial funciona para ello. Encontré una buena publicación sobre la clonación superficial vs profunda aquí para cualquier persona interesada: http://itpksingh.blogspot.com/2009/08/shallow-copyingdeep-copyingobject.html – Sam

+0

Encontré una solución usando ValueInjector. StackOverFlow no me permite "responder mi propia pregunta" todavía. Una vez que lo haga, publicará todos los detalles. – Sam

Respuesta

21

El problema básico que tiene es que tiene que construir una instancia del tipo B (que contiene las propiedades del tipo A). Su enfoque para clonar una instancia A no funcionará, porque eso le da una instancia del tipo A, que no puede convertir a B.

Escribo constructores para las clases A y B que toman un parámetro de tipo A. El constructor de la clase B simplemente pasa el valor a su clase base A.El constructor de la clase A sabe cómo copiar los campos a sí mismo:

class A { 
    public A(A copyMe) { 
     s1 = copyMe.s1; 
     ... 
    } 

class B : A { 

    public B(A aInstance) : base(aInstance) { 
    } 

} 

usarlo de esta manera:

A a = new A(); 
a.s1 = "..."; 

B b = new B(a); 

EDITAR

Cuando no quieren tener que cambiar el constructor de A al agregar nuevos campos o accesorios, puede usar el reflejo para copiar las propiedades. O bien utilizar un atributo personalizado para decorar lo que quiere copiar, o copiar sólo los accesorios/campos de A:

public A (A copyMe) { 
    Type t = copyMe.GetType(); 
    foreach (FieldInfo fieldInf in t.GetFields()) 
    { 
     fieldInf.SetValue(this, fieldInf.GetValue(copyMe)); 
    } 
    foreach (PropertyInfo propInf in t.GetProperties()) 
    { 
     propInf.SetValue(this, propInf.GetValue(copyMe)); 
    } 
} 

que no te has probado el código, pero el punto debería ser clara.

+0

Estoy intentando actualizar esto para que cada miembro no tenga que copiarse individualmente: clase A { public A (A copyMe) { s1 = copyMe.s1; el (A) copyMe.MemberwiseClone() debería devolver un clon superficial de mi llamada A que necesito. Ahora todo lo que tenemos que hacer es devolverlo al constructor B ... la pregunta es cómo. Desafortunadamente, no podemos hacer esto: Clase A { Un público (A copiame) { este = (A) copyMe.MemberwiseClone() }} ... } – Sam

+1

@ Sam: El problema es que tiene que construir un objeto de tipo B. Como A es parte de B, debe encontrar una forma de configurar las propiedades de la nueva instancia B, que pertenece al tipo A. Su * clon A * ¡El enfoque no funcionará porque eso le da una instancia de tipo A, donde necesita una instancia de tipo B! Lo que puedes hacer es usar la reflexión en el constructor de A. Actualizaré mi respuesta para reflejar eso. – Jan

8

Se puede crear un método de copia genérica en la clase A:

 public T Clone<T>() where T : A, new() { 
      return new T() { a = this.a, b = this.b}; 
    } 

O si desea realizar la clonación extensible:

 public T Clone<T>() where T : A, new() { 
      var result = new T(); 
      this.CopyTo(result); 
      return result; 
    } 

    protected virtual void CopyTo(A other) { 
      other.a = this.a; 
      other.b = this.b; 
    } 

Lo usa así:

 A a = new A(); 
    // do stuff with a 
    // Create a B based on A: 
    B b = a.Clone<B>(); 

Tenga en cuenta: en su ejemplo, tanto el nuevo A(), como el MemberwiseClone crearán un nuevo objeto de tipo A.

Si no desea codificar el método de copia usted mismo, podría consultar una herramienta como AutoMapper.

+0

Gracias! En este ejemplo, debería establecer todos los miembros (como a = this.a, b = this.b, etc.). Si la clase tiene varios miembros, de todos modos para evitar la codificación de cada uno? (si decido agregar otro miembro, también tendría que configurarlo en la función de clonación) – Sam

+0

Podrías construir algo con la reflexión (recorrer todos los campos y copiarlos), pero por la experiencia que conozco, será mejor que lo hagas. controle la copia usted mismo. A veces necesitas una copia superficial, para algunos objetos necesitas una copia profunda. – GvS

+0

¿Por qué no 'other = (A) this.MemberwiseClone(); 'trabaja en lo anterior (en lugar de tener que establecer cada propiedad). Estoy tratando de evitar establecer cada propiedad o usar el reflejo. – PeterX

1

Después de jugar y leer todo lo que pude ver, las dos soluciones anteriores de GvS y Jan funcionan. Sin embargo, el resultado final que quería lograr no es ser forzado a escribir cada miembro en los métodos de Copiar.

Por qué: a) Si se edita la clase y se agrega otro objeto, se deberá actualizar el método de copia. Si alguien más actualiza la clase, pueden olvidarse de hacer esto.

b) Puede haber muchos miembros y asignarlos puede llevar mucho tiempo.

c) Simplemente no "se siente" bien. (Probablemente porque soy muy vago).

Afortunadamente, no soy el único con los mismos pensamientos. Encontré una solución muy fácil a través del ValueInjector. (se ha discutido mucho en estos foros).

Después de conseguir la dll (http://valueinjecter.codeplex.com/documentation)

El código se convierte en:

A a = new A(); 
a.s1 = "..."; 


B b = new B(); 
b.InjectFrom(a); 

Eso es todo :)

Obviamente tendría que incluir :

using Omu.ValueInjecter; 

Y no olvides agregarlo a las referencias.

Cuestiones relacionadas