Angella Andrea - Italian Blog

Infinita passione per lo sviluppo software !
posts - 133, comments - 216, trackbacks - 9

My Links

News

MIT OpenCourseWare: I'm invested Wikipedia Affiliate Button


Sto leggendo:

Archives

Post Categories

Siti web realizzati

Siti web tecnici

[70-536] - ICloneable Interface


Area di riferimento

- Developing applications that use system types and collections
    - Implement .NET Framework interfaces to cause components to comply with standard contracts. (Refer System namespace)
        - ICloneable interface


Shallow copy e Deep Copy

Spesso nella pratica si ha la necessità di dover effettuare la copia di un oggetto.

Esistono sostanzialmente due possibilità:

  • Shallow copy
  • Deep copy

La shallow copy effettua una copia parziale dell'oggetto, in particolare vengono copiati solamente i membri value-type mentre gli oggetti figli vengono condivisi con l'oggetto di partenza. In questo caso una modifica alla copia può comportare anche una modifica dell'oggetto da cui è stata costruita e ciò spesso non è un comportamento desiderato.

La deep copy invece comporta una copia integrale dell'oggetto. Il nuovo oggetto creato non ha più niente in comune con il padre.


ICloneable interface

L'interfaccia ICloneable definisce un solo metodo:

[ComVisible(true)]
public interface ICloneable
{
    // Methods
    object Clone();
}


Questo crea una certa confusione in quanto non permette di distinguere se l'implementazione eseguirà una shallow copy oppure una deep copy. E' importante che questa imformazione venga aggiunta alla documentazione. Facciamo un esempio utilizzando i seguenti oggetti:

public class Esame
{
    private string _nome;  // Nome dell'esame
    private int _voto;     // Voto ottenuto dallo studente

    public string Nome
    {
        get { return _nome; }
        set { _nome = value; }
    }

    public int Voto
    {
        get { return _voto; }
        set { _voto = value; }
    }

    public Esame(string nome, int voto)
    {
        _nome = nome;
        _voto = voto;
    }
}

public class Studente
{
    private int _matricola;     
    private string _nome;
    private string _cognome;
    private List<Esame> _esami;  // Esami sostenuti dallo studente

    public Studente(int matricola, string nome, string cognome)
    {
        _matricola = matricola;
        _nome = nome;
        _cognome = cognome;
        _esami = new List<Esame>();
    }

    public Studente(int matricola, string nome, string cognome, List<Esame> esami) : this(matricola, nome, cognome)
    {
        _esami = esami;
    }

    public int Matricola
    {
        get { return _matricola; }
        set { _matricola = value; }
    }

    public string Nome
    {
        get { return _nome; }
        set { _nome = value; }
    }

    public string Cognome
    {
        get { return _cognome; }
        set { _cognome = value; }
    }

    public List<Esame> Esami
    {
        get { return _esami; }
        set { _esami = value; }
    }

    public override string ToString()
    {
        StringBuilder sb = new StringBuilder();
        sb.AppendLine(String.Format("Studente {0} {1} matricola {2} ha sostenuto i seguenti esami:", _nome, _cognome, _matricola));

        foreach(Esame esame in _esami)
        {
            sb.AppendLine(String.Format(" - {0}: {1}", esame.Nome, esame.Voto));
        }

        return sb.ToString();
    }

}


Uno Studente è caratterizzato da un numero di matricola, un nome, un cognome e un elenco degli esami che ha sostenuto con il relativo voto.
Vediamo come implementare l'interfaccia ICloneable in Studente affinchè venga effettuata una shallow copy:

public class Studente : ICloneable
{
    ......

    // Effettua una Shallow Copy dell'oggetto Studente
    public object Clone()
    {
        return this.MemberwiseClone();
    }
}


Per realizzare una shallow copy è quindi sufficiente richiamare il metodo protected MemberwiseClone() appartenente al tipo Object.
Verifichiamo adesso che è stata effettivamente realizzata una shallow copy:

Studente andrea = new Studente(111111, "Andrea", "Angella");
andrea.Esami.Add(new Esame("Fondamenti di Informatica", 27));
andrea.Esami.Add(new Esame("Matematica", 25));

// Effettuo una shallow copy
Studente copia = (Studente) andrea.Clone();

// Modifica della copia per vedere quali campi sono in comune con il padre
copia.Matricola = 222222;
copia.Nome = "Stefano";
copia.Cognome = "D'Onofrio";
copia.Esami[0].Nome = "Calcolatori Elettronici";
copia.Esami[1].Voto = 30;
copia.Esami.Add(new Esame("Elettrotecnica", 19));

// Visualizzo le informazioni sugli studenti
Console.WriteLine(andrea.ToString());
Console.WriteLine(copia.ToString());

Console.ReadKey();


L'output del precedente spezzone di codice è il seguente:

Studente Andrea Angella matricola 111111 ha sostenuto i seguenti esami:
- Calcolatori Elettronici: 27
- Matematica: 30
- Elettrotecnica: 19

Studente Stefano D'Onofrio matricola 222222 ha sostenuto i seguenti esami:
- Calcolatori Elettronici: 27
- Matematica: 30
- Elettrotecnica: 19


Vediamo come implementare l'interfaccia ICloneable in Studente affinchè venga effettuata una deep copy:

[Serializable]
public class Esame
{
    .....
}

[Serializable]
public class Studente : ICloneable
{
    .....
   

    // Effettua una Deep Copy dell'oggetto Studente
    public object Clone()
    {
        MemoryStream ms = new MemoryStream();
        BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));

        // Serializza l'oggetto nello stream in memoria
        bf.Serialize(ms, this);

        // Costruisce un altro oggetto deserializzando i dati nello stream in memoria
        ms.Position = 0;
        object clone = bf.Deserialize(ms);
        ms.Close();

        return clone;
    }
}


Per realizzare una deep copy è possibile sfruttare le tecniche di serializzazione e deserializzazione degli oggetti in memoria. Quando ciò è possibile, questa è una semplice implementazione riusabile.
Verifichiamo adesso che è stata effettivamente realizzata una deep copy analizzando l'output dello stesso spezzone di codice usato per verificare la shallow copy:

Studente Andrea Angella matricola 111111 ha sostenuto i seguenti esami:
- Fondamenti di Informatica: 27
- Matematica: 25

Studente Stefano D'Onofrio matricola 222222 ha sostenuto i seguenti esami:
- Calcolatori Elettronici: 27
- Matematica: 30
- Elettrotecnica: 19


Siccome la necessità di realizzare una deep copy è più alta, io preferisco utilizzare l'interfaccia ICloneable con questa semantica.
Nel caso fosse anche necessaria la possiblità di eseguire una shallow copy allora costruisco una interfaccia personalizzata IShallowCopy e la implemento.

Print | posted on mercoledì 19 dicembre 2007 16:17 | Filed Under [ Exam 70-536 Application Development Foundation ]

Feedback

Gravatar

# re: [70-536] - ICloneable Interface

L'interfaccia ICloneable è sconsigliata, in quanto, quando si copia l'oggetto, restituisce un tipo object e non un tipo tipizato al tipo (scurate il gioco di parole) della classe che si sta clonando.
Ciao
19/12/2007 17:19 | Alessandro Pulvirenti
Gravatar

# re: [70-536] - ICloneable Interface

Quoto Alessandro aggiungendo i link ai perché, detti niente di meno che da Brad Abrams:
http://blogs.msdn.com/brada/archive/2003/04/09/49935.aspx
http://blogs.msdn.com/brada/archive/2004/05/03/125427.aspx
19/12/2007 17:40 | Raffaele Rialdi
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET