2010-03-26 12 views
6

Quiero copiar valores de un objeto a otro. Algo similar a pasar por valor pero con asignación.generador de copia del generador de C#

Por ejemplo:

PushPin newValPushPin = oldPushPin; //I want to break the reference here. 

me dijeron para escribir un constructor de copia para esto. Pero esta clase tiene muchas propiedades, probablemente tomará una hora escribir un constructor de copia a mano.

  1. ¿Hay una mejor manera de asignar un objeto a otro objeto por valor?
  2. Si no, ¿hay un generador de copia de constructor?

Nota: ICloneable no está disponible en Silverlight.

+0

@Brian, la razón para escribir a mano un constructor de copia que toma demasiado tiempo es que necesitaré escribir constructores de copia para las otras clases que usa la clase principal (** PushPin **). –

Respuesta

6

Si puede marcar el objeto que se va a clonar como Serializable, puede usar la serialización en memoria para crear una copia. Compruebe el siguiente código, tiene la ventaja de que funcionará también en otros tipos de objetos y que no tiene que cambiar el constructor de copia ni copiar el código cada vez que se agrega, elimina o cambia una propiedad:

class Program 
    { 
     static void Main(string[] args) 
     { 
      var foo = new Foo(10, "test", new Bar("Detail 1"), new Bar("Detail 2")); 

      var clonedFoo = foo.Clone(); 

      Console.WriteLine("Id {0} Bar count {1}", clonedFoo.Id, clonedFoo.Bars.Count()); 
     } 
    } 

    public static class ClonerExtensions 
    { 
     public static TObject Clone<TObject>(this TObject toClone) 
     { 
      var formatter = new BinaryFormatter(); 

      using (var memoryStream = new MemoryStream()) 
      { 
       formatter.Serialize(memoryStream, toClone); 

       memoryStream.Position = 0; 

       return (TObject) formatter.Deserialize(memoryStream); 
      } 
     } 
    } 

    [Serializable] 
    public class Foo 
    { 
     public int Id { get; private set; } 

     public string Name { get; private set; } 

     public IEnumerable<Bar> Bars { get; private set; } 

     public Foo(int id, string name, params Bar[] bars) 
     { 
      Id = id; 
      Name = name; 
      Bars = bars; 
     } 
    } 

    [Serializable] 
    public class Bar 
    { 
     public string Detail { get; private set; } 

     public Bar(string detail) 
     { 
      Detail = detail; 
     } 
    } 
+3

Eso no va a funcionar en general, porque probablemente habrá referencias a objetos que no deberían copiarse. Hacerlo a mano es la única forma de hacerlo bien (porque eso te permite definir qué significa realmente copiar). Si las clases son lo suficientemente grandes como para ser un problema real, son demasiado grandes. –

+0

@Donal - por no mencionar que puede haber clases referenciadas que no son serializables.Dicho esto, si el OP tiene el control total sobre las clases que se están copiando, así serializadas, definitivamente es una solución adecuada para ellas. – Rob

+0

Veamos ... Etiquetado con Silverlight, por lo que probablemente tenga algunas referencias a instancias de clases que no están bajo control. La copia manual es mejor (escribir un serializador personalizado es la otra alternativa, pero eso es más difícil que hacer la copia). –

2

La única forma (que yo sepa) de hacerlo, y hacerlo correctamente, es implementar la copia usted mismo. Tomemos, por ejemplo:

public class FrobAndState 
{ 
    public Frob Frobber { get; set;} 
    public bool State { get; set; } 
} 
public class Frob 
{ 
    public List<int> Values { get; private set; } 
    public Frob(int[] values) 
    { 
    Values = new List<int>(values); 
    } 
} 

En este ejemplo que había necesidad de saber cómo se implementó FROB, es decir, el hecho de que es necesario llamar al constructor para crear una copia del mismo como valores de sólo lectura está, para poder hacer una copia de una instancia determinada de FrobAndState.

también - se puede aplicar no sólo FrobAndState.Copy así:

public class FrobAndState 
{ 
    // ... Properties 

    public FrobAndState Copy() 
    { 
    var new = new FrobAndState(); 
    new.State = this.State; 
    new.Frobber = this.Frobber; 
    } 
} 

Debido a que tanto la instancia de FrobAndState que llamó .Copy() en, y la nueva instancia ambos tendrían una referencia a la misma instancia de Frobber.

En resumen, copiar cosas es difícil y cualquier implementación de Copia es difícil de hacer bien.

0

Quiero copiar los valores de un objeto a otro objeto. Algo similar para pasar por valor pero con asignación.

¿Qué quiere decir con "with assignment"? Si quiere decir que usted quiere que la gente sea capaz de decir:

a = b; 

Y para que usted pueda definir qué = medios, la única manera de hacerlo que en C# es si b es un tipo diferente de a y' he definido una conversión implícita (o más tenue, si a representa algo de la forma x.Y donde Y es una propiedad con un colocador). No puede anular = para una asignación simple entre tipos idénticos en C#.

Me dijeron que escribiera un constructor de copia para esto.Pero esta clase tiene muchas propiedades de , probablemente tomará una hora escribir un constructor de copia por la mano .

Si eso es realmente cierto, entonces supongo que tiene un problema diferente. Tu clase es muy grande.

0

Si hace su clase Serializable puede Serialize a MemoryStream y Deserialize a una nueva instancia.

0

Si desea realizar una tarea de copia, debe usar un struct en lugar de un class. Pero ten cuidado, es fácil cometer errores sutiles. Se recomienda encarecidamente que todos los diseños sean immutables para reducir las posibilidades de error.

3

Hay un miembro protegido llamado "MemberwiseClone", puede escribir esto en su clase ...

public MyClass Clone(){ 
    return (MyClass)this.MemberwiseClone(); 
} 

entonces usted puede acceder ..

MyClass newObject = oldObject.Clone(); 
0

Sin embargo, esto puede no responder su pregunta directamente, pero para agregar un centavo; generalmente el término Clone está vinculado con shallow copy (objetos referenciados). Para tener una copia profunda, creo que tendrá que buscar en el patrón de algunas creaciones (prototype?). La respuesta a this question podría ayudar.

0

se implementa el método de clonación de objetos de Justin Ángel en Silverlight

using System;

usando System.Reflection;

usando System.Windows;

espacio de nombres JustinAngelNet.Silverlight.Framework

{

public static class SilverlightExtensions 

{ 

    public static T Clone<T>(T source) 
    { 
     T cloned = (T) Activator.CreateInstance(source.GetType()); 

     foreach (PropertyInfo curPropInfo in source.GetType().GetProperties()) 
     { 
      if (curPropInfo.GetGetMethod() != null 
       && (curPropInfo.GetSetMethod() != null)) 
      { 
       // Handle Non-indexer properties 
       if (curPropInfo.Name != "Item") 
       { 
        // get property from source 
        object getValue = curPropInfo.GetGetMethod().Invoke(source, new object[] {}); 

        // clone if needed 
        if (getValue != null && getValue is DependencyObject) 
         getValue = Clone((DependencyObject) getValue); 

        // set property on cloned 
        if (getValue != null) 
        curPropInfo.GetSetMethod().Invoke(cloned, new object[] {getValue}); 
       } 
        // handle indexer 
       else 
       { 
        // get count for indexer 
        int numberofItemInColleciton = 
         (int) 
         curPropInfo.ReflectedType.GetProperty("Count").GetGetMethod().Invoke(source, new object[] {}); 

        // run on indexer 
        for (int i = 0; i < numberofItemInColleciton; i++) 
        { 
         // get item through Indexer 
         object getValue = curPropInfo.GetGetMethod().Invoke(source, new object[] {i}); 

         // clone if needed 
         if (getValue != null && getValue is DependencyObject) 
          getValue = Clone((DependencyObject) getValue); 
         // add item to collection 
         curPropInfo.ReflectedType.GetMethod("Add").Invoke(cloned, new object[] {getValue}); 
        } 
       } 
      } 
     } 

     return cloned; 
    } 
} 

}

A continuación, puede hacer esto

MiClase newObject = SilverlightExtensions.Clone (oldObject);