Technology Experience

Contenuti gestiti da Igor Damiani
posts - 949, comments - 2741, trackbacks - 15120

My Links

News

  • Questo blog si propone di raccogliere riflessioni, teoriche e pratiche, su tutto quello che riguarda il world-computing che mi sta attorno: programmazione in .NET, software attuale e futuro, notizie provenienti dal web, tecnologia in generale, open-source.

    L'idea è quella di lasciare una sorta di patrimonio personale, una raccolta di idee che un giorno potrebbe farmi sorridere, al pensiero di dov'ero e cosa stavo facendo.

    10/05/2005,
    Milano

Archives

Post Categories

Generale

Implementare l'interfaccia ICloneable sui propri business object: che noia!

Se c'è una cosa che odio e che trovo noiosa è implementare l'interfaccia ICloneable sui propri business object. Nulla di complicato, parecchio di noioso, e molto, anche. In fin dei conti si tratta di creare un solo metodo Clone, di creare un nuovo oggetto dello stesso tipo della classe in cui ci si trova, assegnare una ad una tutte le proprietà e ritornare un oggetto con la classica return.

Perchè non farlo fare al nostro bravo (ed estensibile) class designer?

L'estensibilità del class designer di VS2005
Mi sono ispirato all'articolo scritto dal nostro Marco e pubblicato su UGIdotNET. Mi sono cimentato anche io e credo di aver raggiunto un primo risultato che voglio farvi vedere per ricevere commenti, critiche e migliorie. Non sto qui a dilungarmi troppo, d'altronde Marco l'ha spiegato più che bene. Lo dirò solo in breve per chi capita qui per caso, per tutti gli altri documentatevi seguendo l'ottimo articolo già citato prima.

Introduzione
Si tratta di creare una classe che eredita da ClassDiagramCommand e di fare l'override di due metodi: OnInvoke e OnUpdateStatus. Quest'ultimo serve per attivare o no il nostro comando nuovo che, come nell'articolo, viene aggiunto al menù Add del class designer che trovate quando cliccate con il pulsante destro su un membro. Nel mio caso, il comando si attiva quando cliccate su una proprietà nel class designer. Quindi:

protected override void OnUpdateStatus()
{
    
this.Visible = this.Enabled = false;

    
if (Selection.SelectedObjects.Count == 1)
    {
        selectedProperty = Selection.SelectedObjects[0] 
as ClrProperty;
        
if (selectedProperty != null)
            
this.Visible = this.Enabled = true;
    }
}

Attraverso l'oggetto Selection rilevo quanti oggetti ho selezionato. Se ne ho selezionato uno solo, e il cast a ClrProperty mi dà un riferimento diverso da null, allora attivo il comando. Fare l'override del metodo OnInvoke è molto più interessante, perchè ci permette fisicamente di scrivere codice che attraverso un object model dedicato (piuttosto ricco ed articolato, devo dire) modifica il codice sottostante, creando quindi il nuovo metodo Clone() e via dicendo. Vediamo nel dettaglio:

protected override void OnInvoke()
{
    selectedType = Selection.FindFirst<ClrProperty>().ClrType;
    
bool ret = selectedType.ImplementsInterface(INTERFACE_NAME);

    
if(ret)
    {
        MessageBox.Show("This class already implements ICloneable interface!", "Class Designer Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
    
else
    
{
        
// Qui modifico la classe
    
}
}

L'oggetto Selection espone un metodo FindFirst che fa uso di generics. Nel codice qui sopra, faccio una cosa molto semplice: la selezione è su una proprietà (by-design, è stata una scelta mia), quindi da questa ottengo un riferimento al tipo (proprietà ClrType). Il tipo in questo caso non è nient'altro che la classe a cui appartiene la proprietà selezionata. L'oggetto selectedType è quindi di tipo ClrType. Detto questo, controllo attraverso il metodo ImplementsInterface se la classe implementa già l'interfaccia ICloneable. Se mi ritorna true, allora avviso l'utente e la finiamo qui. Altrimenti, proseguiamo con tutto il resto.

Creiamo il codice del metodo Clone
Una volta ottenuto il riferimento al type su cui stiamo lavorando, aggiungere il metodo è di per sè molto semplice. L'object model ci permette di fare tutto senza grossi problemi:

ClrMethod m = selectedType.AddClrMethod(METHOD_NAME);
Selection.DocData.Synchronize();

La seconda riga allinea il file .cs della classe con il metodo Clone appena creato. In questo metodo, l'implementazione di questo metodo non fa altro che sollevare un'Exception. Andiamo oltre. Ottengo un riferimento ad un oggetto CodeElement che punti al metodo Clone: per farlo faccio un loop e controllo la proprietà Name di ciascun CodeElement.

Per scrivere realmente il codice di Clone, ho scritto una function privata createMemberCode, che fra le altre cose fa quanto segue:

  1. dichiara un oggetto ret che verrà ritornato dal metodo
  2. ret è dello stesso tipo della classe di cui stiamo implementando l'interfaccia ICloneable
  3. fa un loop su tutte le proprietà definite nella classe e le riassegna all'oggetto ret, ottenendo così un clone vero e proprio
  4. infine, faccio una semplice return(ret);

Fatto questo, fatto tutto. Ci sono alcune cose che devo mettere a posto. Prima cosa: se la classe implementa ICloneable, la sua dichiarazione deve indicarlo, cosa che per adesso non riesco a fare. Difatti, la MessageBox di conferma avvisa lo sviluppatore che deve scrivere a mano ": ICloneable" nella dichiarazione della classe. Seconda cosa: se aggiungiamo una proprietà alla classe dopo aver implementato l'interfaccia, il metodo Clone non è più allineato e quindi la nuova proprietà non viene clonata. Terza cosa: il clone non fa viene creato tramite una deep-copy: se una proprietà è a sua volta un oggetto di un'altra classe, devo chiamare la relativa Clone. Quarta cosa: tutto quello che adesso non mi viene in mente.

Download dei sorgenti
Il download dei sorgenti è disponibile qui. E' sufficiente compilarlo e copiare i files ICloneableInterface.* nella directory %APP_DATA%\Microsoft\MSEnvShared\Addins. Lo miglioriamo insieme?

powered by IMHO 1.2

Print | posted on Tuesday, June 6, 2006 2:36 PM | Filed Under [ I miei freeware ]

Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET