2010-03-26 63 views
6

Voglio copiare i valori da un oggetto a un altro oggetto. Qualcosa di simile a passare per valore ma con incarico.C# generatore costruttore copia

Ad esempio:

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

mi è stato detto di scrivere un costruttore di copia per questo. Ma questa classe ha molte proprietà, probabilmente ci vorrà un'ora per scrivere a mano un costruttore di copie.

  1. C'è un modo migliore per assegnare un oggetto a un altro oggetto in base al valore?
  2. In caso contrario, esiste un generatore di costruttore di copie?

Nota: ICloneable non è disponibile in Silverlight.

+0

@Brian, il motivo per cui la scrittura manuale di un costruttore di copie richiede troppo tempo è che avrò bisogno di scrivere i costruttori di copia per le altre classi utilizzate dalla classe principale (** PushPin **). –

risposta

6

Se è possibile contrassegnare l'oggetto da clonare come serializzabile, è possibile utilizzare la serializzazione in memoria per creare una copia. Controlla il seguente codice, ha il vantaggio che funzionerà anche su altri tipi di oggetti e che non devi modificare il tuo costruttore di copia o copiare il codice ogni volta che una proprietà viene aggiunta, rimossa o modificata:

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

Non funzionerà in generale, perché probabilmente ci saranno riferimenti ad oggetti che non dovrebbero essere copiati. Farlo a mano è l'unico modo per farlo bene (perché questo ti consente di definire cosa significhi realmente copiare). Se le classi sono abbastanza grandi che questo è un problema reale, sono troppo grandi. –

+0

@Donal - per non parlare delle classi di riferimento che non sono serializzabili.Detto questo, se l'OP ha il pieno controllo sulle classi che vengono copiate, quindi serializzate, è sicuramente una soluzione adeguata per loro. – Rob

+0

Vediamo ... Contrassegnati con Silverlight, quindi probabilmente alcuni riferimenti a istanze di classi non sono sotto controllo. La copia manuale è migliore (la scrittura di un serializzatore personalizzato è l'altra alternativa, ma è più difficile rispetto alla copia). –

2

L'unico modo (di cui sono a conoscenza) per farlo e farlo correttamente è implementare la copia da soli. Prendiamo ad esempio:

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); 
    } 
} 

In questo esempio avresti bisogno di sapere come è stato implementato Frob, vale a dire il fatto che è necessario chiamare il costruttore per creare una copia di esso come valori è di sola lettura, essere in grado di fare una copia di una determinata istanza di FrobAndState.

Inoltre - si potrebbe non solo implementare FrobAndState.Copy così:

public class FrobAndState 
{ 
    // ... Properties 

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

Poiché sia ​​l'istanza di FrobAndState che hai chiamato .copy(), e la nuova istanza sarebbero entrambi hanno un riferimento al stessa istanza di Frobber.

In breve, copiare le cose è difficile e qualsiasi implementazione di copia è difficile da ottenere.

0

voglio copiare i valori da un oggetto a un altro oggetto. Qualcosa di simile da passare per valore ma con assegnazione.

Cosa intendi per "con incarico"? Se vuoi dire che si desidera che le persone siano in grado di dire:

a = b; 

E per voi per definire ciò che = mezzi, l'unico modo è possibile farlo in C# è se b è un tipo diverso di a e si' Ho definito una conversione implicita (o più tenue, se a sta per qualcosa del modulo x.Y dove Y è una proprietà con un setter). Non è possibile sovrascrivere = per un semplice compito tra tipi identici in C#.

Mi è stato detto di scrivere una copia del costruttore per questo.Ma questa classe ha molte proprietà di , probabilmente impiegherà un'ora per scrivere un costruttore di copia per mano .

Se è proprio vero, allora suppongo che tu abbia un problema diverso. La tua classe è troppo grande.

0

Se si rende la classe Serializable è possibile Serialize a un MemoryStream e Deserialize in una nuova istanza.

0

Se si desidera eseguire la copia su assegnazione, utilizzare struct anziché class. Ma attenzione, è facile commettere errori impercettibili. Si raccomanda vivamente che tutti gli stent siano immutabili per ridurre la possibilità di errore.

3

C'è un membro protetta denominata "MemberwiseClone", è possibile scrivere questo nella tua classe ...

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

poi si può accedere ..

MyClass newObject = oldObject.Clone(); 
0

Anche se, questo potrebbe non rispondere la tua domanda direttamente, ma per aggiungere un centesimo; di solito il termine Clone è collegato a shallow copy (oggetti di riferimento). Per avere una copia profonda, credo che sarà necessario esaminare il modello creativo (prototype?). La risposta a this question potrebbe aiutare.

0

Si implementa il metodo di oggetti clonazione di Justin Angel in Silverlight

using System;

utilizzando System.Reflection;

utilizzando System.Windows;

namespace 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; 
    } 
} 

}

Poi si può fare questo

MyClass newObject = SilverlightExtensions.Clone (oldObject);

Problemi correlati