2010-03-30 9 views
16

Data la seguente classe:Come copia completa una classe senza la marcatura come Serializable

class A 
{ 
    public List<B> ListB; 

    // etc... 
} 

dove B è un'altra classe che può ereditare/contenere alcune altre classi.


Dato questo scenario:

  1. A è una grande classe e contiene molti tipi di riferimento
  2. non riesco a segnare B come [Serializable] come non ho accesso al codice sorgente di B

Quanto segue metodi per eseguire una copia profonda non funzionano:

  1. non posso usare ICloneable o MemberwiseClone come classe A contiene molti tipi di riferimento
  2. non posso scrivere un costruttore di copia per A, come la classe è grande e continuamente aggiunti , e contiene le classi (come B) che non possono essere copiati profonda
  3. non posso usare la serializzazione come non riesco a segnare una classe contenuta (come B, dove nessun codice sorgente disponibile) come [Serializable]

Come è possibile copiare in profondità la classe A?

+0

@Will: Il mio Sympathie, stavo cercando di riformattare, anche, che casino! lexu

+0

Grazie amico, in realtà sono nuovo di questo sito, ho formattato durante la digitazione, ma quando ho postato è venuto come tht – Gaddigesh

+0

questo lo fa senza serializzazione: http://valueinjecter.codeplex.com/wikipage?title=Deep%20Cloning&referringTitle= Home – Omu

risposta

7

ho smesso di usare la serializzazione per la copia profonda in ogni caso, perché non c'è abbastanza controllo (non ogni classe deve essere copiato allo stesso modo). Poi ho iniziato a implementare le mie interfacce di copia profonda e copiare ogni proprietà nel modo in cui dovrebbe essere copiata.

modi tipici per copiare un tipo di riferimento:

  • uso costruttore di copia
  • metodo fabbrica
  • utilizzo (es. Tipi immutabili)
  • utilizzare il proprio "clone"
  • copiare solo di riferimento (ad esempio, altri tipi di root)
  • creare una nuova istanza e copiare le proprietà (ad es.tipi non scritti da te manca un costruttore di copia)

Esempio:

class A 
{ 
    // copy constructor 
    public A(A copy) {} 
} 

// a referenced class implementing 
class B : IDeepCopy 
{ 
    object Copy() { return new B(); } 
} 

class C : IDeepCopy 
{ 
    A A; 
    B B; 
    object Copy() 
    { 
    C copy = new C(); 

    // copy property by property in a appropriate way 
    copy.A = new A(this.A); 
    copy.B = this.B.Copy(); 
    } 
} 

si potrebbe pensare che questo è un enorme quantità di lavoro. Ma alla fine, è facile e diretto, può essere sintonizzato dove serve e fa esattamente quello che ti serve.

+0

Cosa succede se hai un oggetto grande? Per favore non dirmi che avere un grosso oggetto è il problema, perché questo crea solo uno più grande. –

+0

@Bomboca ;: Ovviamente, è necessario il codice per la copia profonda esplicita da qualche parte. Ma non c'è altra soluzione pulita. Se esistesse un'altra soluzione generica, potrebbe essere molto traballante o richiedere un notevole sforzo di configurazione, che lo rende meno trasparente e molto più difficile da controllare. Quindi qual è il problema con la semplice copia dei valori, il modo esatto in cui si desidera copiarli? –

+0

Apprezzo il tuo punto di vista, ma è troppo lavoro per impostare la copia di un grande oggetto. Poi qualcun altro aggiunge un nuovo campo e dimentica o non sa nemmeno della cosa copia.Il QA arriva e dice che non funziona. Certo, ci lavoreremo, ma farlo in modo generico garantisce più prove future. Essere meno trasparenti è il prezzo da pagare per le convenzioni e modi generici di fare le cose, ma questo è necessario per astrarre operazioni di livello inferiore in modo che possiamo concentrarci sulla risoluzione dei problemi in modo efficiente. Accetterei comunque la tua soluzione per classi molto piccole e piccole. –

0

La serializzazione XML non ti può aiutare?

+0

Sembra che il suo progetto sia così rovinato che potrebbe soffocarsi. Un semplice dizionario nell'albero degli oggetti e la serializzazione xml (beh, il XmlSerializer) potrebbe soffocare. – Will

1

Non puoi farlo?

[Serializable] 
class A 
{ 
    ... 
    [NonSerialized] 
    public List<B> ListB; 
    .... 
} 

E poi fare riferimento a How do you do a deep copy of an object in .NET (C# specifically)? per una funzione di clonazione

+0

@m_oLogin: L'OP lo desidera senza contrassegnarlo 'Serializable'. –

+0

Da quello che ho capito, l'autore afferma che non può contrassegnare la classe interna come [Serializable] perché non ha accesso al codice sorgente ... Può ancora contrassegnare il membro "ListB" interno come [NonSerialized] e quindi utilizzare la serializzazione per la copia profonda. – karlipoppins

1

l'interfaccia IDeepCopy è esattamente ciò che specifica ICloneable.

class B : ICloneable 
{ 
    public object Clone() { return new B(); } 
} 

e con più implementazione amichevole:

class B : ICloneable 
{ 
    public B Clone() { return new B(); } 
    // explicit implementation of ICloneable 
    object ICloneable.Clone() { return this.Clone(); } 
} 
+3

-1 IConeable non significa che è una copia profonda. Ci sono classi in .Net che sono contrassegnate come ICloneable ma non fanno una copia profonda. – Amir

4

si può provare questo. Funziona per me

public static object DeepCopy(object obj) 
    { 
     if (obj == null) 
      return null; 
     Type type = obj.GetType(); 

     if (type.IsValueType || type == typeof(string)) 
     { 
      return obj; 
     } 
     else if (type.IsArray) 
     { 
      Type elementType = Type.GetType(
       type.FullName.Replace("[]", string.Empty)); 
      var array = obj as Array; 
      Array copied = Array.CreateInstance(elementType, array.Length); 
      for (int i = 0; i < array.Length; i++) 
      { 
       copied.SetValue(DeepCopy(array.GetValue(i)), i); 
      } 
      return Convert.ChangeType(copied, obj.GetType()); 
     } 
     else if (type.IsClass) 
     { 

      object toret = Activator.CreateInstance(obj.GetType()); 
      FieldInfo[] fields = type.GetFields(BindingFlags.Public | 
         BindingFlags.NonPublic | BindingFlags.Instance); 
      foreach (FieldInfo field in fields) 
      { 
       object fieldValue = field.GetValue(obj); 
       if (fieldValue == null) 
        continue; 
       field.SetValue(toret, DeepCopy(fieldValue)); 
      } 
      return toret; 
     } 
     else 
      throw new ArgumentException("Unknown type"); 
    } 

Grazie alla DetoX83 article sul progetto di codice.

+0

non funziona con i gestori di eventi. –

+0

Lotto di "gotchas" qui. Enums, nullables, ecc ... – OnResolve

+0

La riga Type elementType = Type.GetType ( type.FullName.Replace ("[]", string.Empty)); deve essere sostituito con: Type elementType = type.GetElementType(); – Karrok

2
private interface IDeepCopy<T> where T : class 
    { 
     T DeepCopy(); 
    } 

    private class MyClass : IDeepCopy<MyClass> 
    { 
     public MyClass DeepCopy() 
     { 
      return (MyClass)this.MemberwiseClone(); 
     } 
    } 

Pluss: Yoy in grado di controllare il processo di copia (se la vostra classe ha proprietà identificatore si possono impostare, oppure è possibile scrivere altro codice logica di business)


Minus: classe può essere contrassegnato come sigillato


+4

In che modo MemberwiseClone() diventa una copia profonda? È un codice di esempio, solo? Se sì, perché non è contrassegnato come? Perché fornire un codice di esempio non valido e contrassegnato, se esiste già una risposta con un buon codice di esempio? Chi diavolo ha svalutato questo? –

-1

Un answer da un thread diverso che noi la serializzazione di json è la migliore che abbia mai visto.

public static T CloneJson<T>(this T source) 
{  
    if (Object.ReferenceEquals(source, null)) 
    { 
     return default(T); 
    }  
    return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source)); 
} 
0

Provare a usare un flusso di memoria per ottenere una copia completa del vostro oggetto:

public static T MyDeepCopy<T>(this T source) 
      { 
       try 
       { 

        //Throw if passed object has nothing 
        if (source == null) { throw new Exception("Null Object cannot be cloned"); } 

        // Don't serialize a null object, simply return the default for that object 
        if (Object.ReferenceEquals(source, null)) 
        { 
         return default(T); 
        } 

        //variable declaration 
        T copy; 
        var obj = new DataContractSerializer(typeof(T)); 
        using (var memStream = new MemoryStream()) 
        { 
         obj.WriteObject(memStream, source); 
         memStream.Seek(0, SeekOrigin.Begin); 
         copy = (T)obj.ReadObject(memStream); 
        } 
        return copy; 
       } 
       catch (Exception) 
       { 
        throw; 
       } 
      } 

Here is more.

+0

Il titolo è "Come copiare in profondità una classe senza contrassegnarla come serializzabile". La tua classe deve essere serializzabile per te per serializzarla con un MemoryStream. –

+0

@Andy_Vullhop ho provato con diverse classi (non serializzabile) e anche la mia semplice classe di esempio. In nessuno dei casi è richiesta una clausola "serializzabile". Tuttavia, ammetto, che ottieni un'eccezione a meno che la tua classe serializzata non sia contrassegnata come "DataContractAttribute". E tutti i membri che desideri serializzare devono essere contrassegnati con "DataMemberAttribute". Se la classe è una raccolta, devi contrassegnarla come "CollectionDataContractAttribute". In realtà ricevi un messaggio di eccezione che lo reclama. Penso che sia abbastanza giusto ... –

+0

Sì, e segnando la classe originale qualsiasi cosa - lo riporta all'inizio ..: o / –

Problemi correlati