Classi e OOP introduzione (parte 2/4)
a cura di Enrico Barillari e Sabrina Cosolo (requisiti: minima esperienza di programmazione)La nostra classe esempio, finalmente:
Per iniziare a costruire la prima classe, aperto il file di testo con estensione CS creato da Visual Studio, occorre prima di tutto definire un namespace:
Per creare un namespace possiamo inserire la parola chiave appunto namespace, ed un nome identificativo del nostro spazio dei nomi, seguito da una coppia di parentesi graffe, che ne indicano l'ambito, e ne racchiudono i membri.namespace MyNamespace
{
//... qui definiremo la nostra classe
}Iniziamo la stesura della nostra classe base di esempio, la classica classe Persona di immediata comprensione adatta alla dimostrazione di ereditarietà, polimorfismo e quanto da noi precedentemente introdotto. Se avete creato il progetto esempio seguendo le istruzioni indicate alla pagina precedente, allora il namespace della classe Persona sarà già corretto; qui sotto, inseriamo la prima porzione di codice che poi commenteremo.
namespace MyNamespace
{
public class Persona
{
private string mNome;
private string mCognome;
protected string mIndirizzo;
protected string mCitta;
protected string mDataNascita;
public Persona()
{
this.mNome = "";
this.mCognome = "";
this.mIndirizzo = "";
this.mCitta = "";
this.mDataNascita = "";
}
}
}Prima della discussione relativa a quello che abbiamo scritto nel codice della classe, inseriamo un Consiglio da buon programmatore «zen»:
Regola principe per la scrittura di codice leggibile - che noterete nei piccoli segmenti di codice già scritti e in quelli che scriveremo: il codice deve essere indentato, ogni istruzione va scritta su una riga separata, se è troppo lunga per essere visibile su una schermata media deve essere spezzata su più righe. Per i Csciarpisti in erba, imparate a mettere le parentesi graffe su una riga separata all'interno del codice, le parentesi graffe in fondo alle righe confondono la vista e se pure diminuiscono il numero di righe scritte sono deleterie quando si deve andare a capire come funziona il codice scritto da qualcun altro, perché non si capisce dove iniziano e finiscono gli statement.Il codice qui sopra trascritto ci mostra una classe base funzionante, essa ha al suo interno una serie di dichiarazioni di variabili member, e un Costruttore che si premura di inizializzare le variabili member stesse. E' una classe funzionante, ma poco funzionale: infatti può essere instanziata, ma non ha una grande utilità. Ci permette però di dare una definizione precisa di che cos'è il costruttore e del concetto di visibilità dei membri di una classe.
Il costruttore
Il costruttore della classe è una funzione pubblica e non ha alcun valore di ritorno, neppure void (il valore di ritorno è il tipo del dato che ogni metodo di una classe deve restituire oppure void per i metodi che non restituiscono un valore - le sub di VB -), il valore di ritorno può essere ad esempio int, string o qualsiasi altro tipo base o definito dall'utente.
Il costruttore di una classe in C# assume lo stesso nome della classe; in una classe possono essere definiti più costruttori con diversi parametri (il polimorfismo o overload); il costruttore ha la funzione di inizializzare tutto ciò che serve a rendere la classe utilizzabile a chi l'ha instanziata.Della visibilità
Come accennato, in questa prima porzione di codice abbiamo introdotto un altro dei concetti base relativi alle classi in OOP, il concetto di visibilità.
All'interno di una classe, i suoi componenti - siano essi variabili member, metodi, proprietà, eventi - possono avere diversi ambiti di visibilità, principalmente public e private, a cui si aggiunge protected.
Un elemento public è direttamente visibile ed accessibile dal metodo che instanzia la classe, ecco perché le variabili membro della classe non sono usualmente public, ma la possibilità di modificarle sarà esposta in modo diverso usando metodi e proprietà (vedasi incapsulamento), il costruttore della classe è invece public perché altrimenti la classe non può essere instanziata.
Un elemento private è invece visibile solo all'interno della classe in cui è definito.
In una classe ben scritta, troveremo proprietà, metodi, eventi public e variabili member private o al limite protected, ma troveremo anche metodi proprietà ed eventi private o protected, quando questi saranno ad uso esclusivo della classe e non dovranno essere visibili al suo esterno.
Il livello di protezione protected è una via di mezzo tra public e private; infatti, rende la proprietà, il metodo o la variabile non accessibile dall'esterno, ma accessibile da una eventuale classe erede. Il modificatore public o private a livello di classe, modifica il modo in cui sono trattati i suoi componenti public e protected nelle classi da essa derivate.
Una classe all'interno di un Assembly può avere diversi ambiti di visibilità, può essere public (pubblica), quindi nel caso di una DLL essere visibile ai programmi che referenzieranno quella DLL che potranno usarla e manipolarla in modo diretto. Può essere private (privata) e quindi visibile solo all'interno dell'assembly che la ospita.
Ci sono altri due livelli di protezione che possono essere utilizzati all'interno di un Assembly: internal rende l'oggetto (in questo caso una classe) accessibile solo all'interno dell'assembly che la ospita. protected internal rende l'oggetto accessibile all'interno dello stesso assembly, ma ai soli oggetti derivati. Non approfondiamo questi concetti in quanto sono alquanto complessi, e la loro discussione andrà effettuata nell'ambito di un articolo dedicato ai programmatori con più esperienza. Per ora cerchiamo di focalizzare il concetto di public e private per un elemento di una classe e provare a verificare cosa significa.Costruttori polimorfici (overload)
Proseguiamo lo sviluppo della nostra classe e andiamo ad aggiungere un po' di codice alla classe Persona.
Per prima cosa vediamo come inizializzare i valori dei campi privati, che serviranno a gestire le informazioni interne, ed il cui accesso all'esterno della classe sarà regolato dalle proprietà pubbliche.
Il modo più semplice per valorizzare questi campi è attraverso l'uso di parametri aggiunti al costruttore e quindi andiamo a realizzare i nostri primi costruttori polimorfici (no, non mordono non vi preoccupate...), generando delle funzioni di «overload» del costruttore della classe Persona.
Non so bene il motivo della parola sovraccarico, usata per indicare le funzioni polimorfiche, ma forse è perché «polimorph» aveva un suono poco simpatico.
Oltre al costruttore predefinito, senza argomenti, possiamo quindi definire uno o più costruttori che accettino i parametri necessari a valorizzare i nostri campi privati, nel nostro esempio ne faremo due:public Persona(string pNome, string pCognome, string pIndirizzo,
string pCitta, string pDataNascita)
{ this.mNome = pNome; this.mCognome = pCognome; this.mIndirizzo = pIndirizzo;
this.mCitta = pCitta;
this.mDataNascita = pDataNascita;
} public Persona(string pNome, string pCognome)
{
this.mNome = pNome;
this.mCognome = pCognome;
this.mIndirizzo = "";
this.mCitta = "";
this.mDataNascita = "";
}Facciamo notare subito un vezzo che non è obbligatorio seguire, ma che vi invitiamo a seguire, eventualmente personalizzato a vostro piacimento, perhé si rivela molto utile nel momento in cui leggiamo una porzione di codice particolarmente complesso, non di certo una semplice assegnazione come in questo caso.
Se non vi avete ancora fatto caso, osservate come tutte le variabili member della classe si chiamino mNomeVariabile, mentre tutti i parametri si chiamano pNomeParametro. Non è una cosa casuale ma voluta, perché in questo modo sappiamo immediatamente, dall'interno di una qualsiasi funzione che osserviamo nel sorgente di una classe magari complessa, se stiamo utilizzando una variabile visibile a livello di classe oppure un parametro passato dal chiamante. Nonostante Intellisense sia straordinariamente utile e possa darci informazioni precise sul tipo e la forma di una variabile, un aiutino in più rende più facile capire il funzionamento e spesso trovare errori logici, quelli che fanno sì che come sempre il programma che avete scritto fa quello che gli dite non quello che volete.this, get, set
Un'altra cosa che abbiamo usato copiosamente in questa porzione di codice è la parolina this. Per chi viene da Java o C++ o Javascript non è difficile, per chi viene da VB è il corrispondente del Me; per chi invece è un principiante il concetto di this è un po' complesso. Speriamo di spiegarlo per intero man mano che tratteremo il resto degli argomenti, l'assegnazione o l'uso di un membro di una classe preceduto dalla parola this indica che vi riferite in modo specifico a 'questo' specifico oggetto istanza della classe (lo so sembra difficile, ma diventerà meno difficile quando introdurremo la parola base).
Non sempre un metodo costruttore o comunque un qualsiasi metodo di una classe che abbia un numero elevato di parametri è il miglior modo per costruire del codice, anche se a volte può essere utile.
Quando per utilizzare una classe complessa è necessario inizializzarne molti membri è più pulito creare un costruttore senza parametri che effettua il semplice «clear» dei dati e inizializzare tutti i valori utilizzando le proprietà pubbliche: l'opportuna validazione dei dati necessari metodo per metodo farà in modo di guidare il programmatore al corretto uso della classe.
Abbiamo parlato di proprietà pubbliche, quindi aggiungiamone qualcuna alla nostra classe; le proprietà pubbliche sono il metodo più corretto di accesso ai membri privati di una classe, con i loro metodi accessori get e set, tramite i quali si può regolamentare l'accesso alle stesse, per esempio definendo delle regole di validazione, oppure per rendere la variabile member di sola lettura oppure di sola scrittura.
Scriviamo ora un primo esempio di proprietà per cui creeremo anche una funzione di validazione del dato inserito.
public string Nome { get { return this.mNome; } set { if(TestNome(value)) { this.mNome = value; } else { throw new ApplicationException ("Sono accettate solo lettere "); } } } private bool TestNome(string pNome) { bool ret = false; Regex reg = new Regex("[^a-zA-Z]"); Match match = reg.Match(name); if(match.Success) { ret = true; } return ret; }Come è possibile vedere, la proprietà è un tipo particolare di metodo che viene definita con la sintassi:
modificatoredivisibilità tipo Nome
senza le parentesi tonde che hanno tutti i metodi, vuote oppure con l'elenco dei parametri. Dopo la definizione creiamo uno «statement» usando due parentesi graffe e al suo interno inseriamo due metodi accessori chiamati convenzionalmente get e set al cui interno inseriremo le funzionalità per restituire il valore della variabile membro o per inizializzarlo, dopo gli opportuni controlli di validazione. Il metodo set fornisce il valore da inserire nella variabile membro in un campo convenzionalmente chiamato value e non definito a livello di funzione, a differenza di VB in cui il campo value deve essere predisposto dal programmatore.
Per dimostrare come validare un valore prima di inserirlo in una variabile membro, abbiamo creato anche un metodo che ne testa il contenuto usando una regular expression; come potete vedere, il metodo ha visibilità private poiché serve solo all'interno della classe, e al contrario del costruttore ha un tipo di ritorno, bool; la funzione effettua una validazione un po' selettiva, dato che non ci permette di inserire che lettere, ma per un nome proprio potrebbe essere sufficiente a meno che non vi chiamiate Carlo Maria o Carlo Azeglio. Se però copiate e incollate questo codice nella vostra classe e la compilate, otterrete una serie di errori di questo tipo:
<percorso>\Persona\Persona.cs(62): The type or namespace name 'Regex' could not be found (are you missing a using directive or an assembly reference?)
Questo ci permette di introdurre un altro concetto relativo alle classi del .NET Framework.
Abbiamo parlato all'inizio dell'articolo di namespace, e abbiamo accennato al fatto che il .NET Framework mette a disposizione una serie di funzionalità già pronte che possiamo usare all'interno dei nostri programmi. Questo è realizzato tramite una serie estesa di classi suddivise in namespace logici.
La maggior parte delle classi si trovano in System e nei suoi namespace child.System.Windows.Forms System.Drawing System.Xml System.IOsono alcuni dei namespace ma ve ne sono moltissimi altri di cui trovate documentazione estesa su MSDN.
Per referenziare una classe che si trova in un determinato namespace è necessario dichiararla con il suo «path» completo composto dai namespace nidificati che arrivano fino a quello che la ospita; per evitare l'errore che abbiamo evidenziato la funzione TestNome diverrebbe:private bool TestNome(string pNome) { bool ret = false; System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("[^a-zA-Z]"); System.Text.RegularExpressions.Match match = reg.Match(name); if(match.Success) { ret = true; } return ret; }using
Un po' poco leggibile direte voi, come sicuramente hanno urlato i programmatori Microsoft agli sviluppatori del Framework quando lo hanno scoperto. Per questo, è stata introdotta la parola chiave using. Per chi avesse nozioni di C o C++ assomiglia molto a #include.
Vediamo come si usa:
se in testata al nostro file .cs prima della definizione del namespace inseriamo le seguenti righe:using System; using System.Text; using System.Text.RegularExpressions;Magicamente tornando alla prima versione della funzione di test l'errore di compilazione sparisce.
Possiamo quindi affermare che la clausola using inserita per i namespace utilizzati all'interno di una classe, permette di riferirsi a tutte le classi in essi contenute senza dover compilare il «path» completo del namespace ospitante. Fate solo attenzione ad un particolare importante: se inserite la clausola using per due diversi namespace con una classe con lo stesso nome, dovete comunque indicare il path della classe che volete instanziare per essere certi che sia quella giusta.
Definiamo ora un paio di proprietà di sola scrittura, semplicemente evitando di definire il metodo accessorio get:
public string Indirizzo { set { this.mIndirizzo = value; } } public string Citta { set { if(TestNome(value)) { this.mCitta = value; } else { throw new ApplicationException ("Sono accettate solo lettere"); } } }Seconda nota per il programmatore «zen»: la città è una variabile che ci permette di esprimere un'altra perla di saggezza programmatoria derivata dall'esperienza accumulata fin dai tempi degli XTSaurus e ATSaurus. Abbiamo volutamente definito la parola senza l'accento mCitta e Citta in quanto è altamente sconsigliabile - a livello di programmi, così come a livello di nomi di campo in database relazionali, e comunque a livello di nomenclatura in generale - definire nomi di variabili che contengano lettere accentate specifiche di una lingua: personalmente sconsiglio vivamente di utilizzare qualcosa di diverso da lettere base non accentate maiuscole e minuscole e cifre (ammesse purché dopo il primo carattere). Motivo? Probabilmente non accadrà mai, ma se per qualsiasi caso doveste aprire un programma con le lettere accentate da un PC di diversa cultura e compilarlo, probabilmente il numero di parolacce pronunciate da voi o dal disgraziato che dovesse farlo farebbe sì che vi si spalancassero sotto i piedi i cancelli dell'ade e veniste immediatamente inghiottiti fino all'ultimo livello dei gironi infernali.
Si tratta comunque di opinioni personali che siamo certi i maniaci dei Nomi_separati_da_underscore non approveranno, ma per chi principia può tornare utile. (Ricordo ai maniaci degli underscore che in SQL _ equivale al ? del DOS ovvero è un carattere jolly, e quindi i nomi di campo che lo contenessero dovrebbero sempre essere utilizzati con le parentesi quadre). Ma chiudiamo questa parentesi sulla filosofia della nomenclatura e torniamo a noi.Definiamo una proprietà di sola lettura, definendo il solo metodo accessorio get:
public string IndirizzoCompleto { get { StringBuilder sb = new StringBuilder(""); if( this.mIndirizzo.Length > 0 ) { sb.Append( this.mIndirizzo ); sb.Append( "\n" ); } if( this.mCitta.Length > 0 ) { sb.Append( this.mCitta ); } return( sb.ToString() ); } }Come potete notare, questa property non restituisce il semplice valore di una variabile, ma un valore costruito a partire da più variabili member.
Definiamo invece un'altra proprietà con accesso in lettura e scrittura e anche per questa inseriamo delle regole di validazione.
public string DataNascita { get { return this.dataNascita; } set { if(TestData(value)) { this.dataNascita = value; } else { throw new ApplicationException (value + " non é una data valida” + ”\nInserisci una data valida” + ” in formato dd/MM/yyyy"); } } }Scriviamo ora il metodo per la validazione della data utilizzando una regular expression; come il precedente metodo di validazione per il nome è privato in quanto non serve all'esterno della classe:
private bool TestData(string pData) { bool ret = false; Regex reg = new Regex(@"(?n:^(?=\d)((?31(?!(.0?[2469]|11))” + ”|30(?!.0?2)|29(?(.0?2)(?=.{ 3,4}(1[6-9]|[2-“ + ”9]\d)(0[48]|[2468][048]|[13579][26])|(16|[2468][048]” + ”|[3579][26])00))|0?[1-9]|1\d|2[0-8])(? [/.-])(? 0?[1-“ + ”9]|1[012])\2(? (1[6-9]|[2-9]\d)\d{2})” + ”(?:(?=\x20\d)\x20|$))?(? Scriviamo invece un metodo pubblico che ci calcola e restituisce l'età della persona:
public int Eta() { if(this.mDataNascita != String.Empty) { DateTime nascita = DateTime.Parse(this.mDataNascita); DateTime now = DateTime.Now; TimeSpan ts = now.Subtract(nascita); return ts.Days / 365; } return 0; }Questo metodo non è proprio precisissimo, infatti non tiene conto degli anni bisestili e quindi negli immediati dintorni del compleanno darà un anno in più o in meno, ma un dato attendibile lo fornisce, potremo in seguito sbizzarrirci nella creazione di una funzione più esatta.
Override
Terminiamo la stesura della nostra classe base introducendo un altro dei concetti di OOP applicabile ed utilizzabile, il concetto di Sovrascrittura (override) di un elemento della classe genitrice da parte di una qualsiasi classe erede.
Anche se, come la stiamo creando, la nostra classe Persona non è figlia o erede di alcuna altra classe, dobbiamo considerare che all'interno del .NET Framework tutte le classi senza un genitore ereditano dalla classe base Object.
La classe Object possiede al suo interno una serie di metodi che tutte le classi ereditano di conseguenza, fra questi il più noto e il più usato è metodo ToString, che, se non implementato diversamente, restituisce una stringa composta dallo spazio dei nomi, e dal nome della classe.
Volendo fare in modo che per la nostra classe il metodo ToString fornisca informazioni più utili e precisamente un estratto dei dati presenti nelle variabili member della classe, possiamo modificare questo metodo, per fargli restituire le informazioni che riteniamo opportune; per fare questo, implementiamo un metodo con lo stesso nome (ToString) del metodo base, e indichiamo al Framework che il nostro metodo si «sovrappone» al metodo originale, utilizziando la parola chiave override inserita come attributo al metodo prima del suo tipo di ritorno.public override string ToString() { return "Nome: " + this.Nome + " " + this.Cognome +
"\nIndirizzo:" + this.Indirizzo + "\nCittà: " + this.Citta + "\nEtà: " + this.Eta(); }All'opera
Abbiamo ottenuto in questo modo la nostra classe base, perciò vediamo cosa fare per poterla utilizzare:
Per prima cosa aggiungiamo una form al progetto di prova in Visual Studio cliccando con il tasto destro sul progetto Persona e selezionando Add, Add Windows Form e chiamiamo la nuova form frmPersona.
Gli cambiamo opportunamente titolo e inseriamo una label che allargheremo in modo da riempire la form e a cui daremo un colore di Background ed un colore del font a piacere.
A questo punto, torniamo sul designer della form frmMenu e facciamo doppio click sul menuItem mnuPersona, e nel suo evento Click scriviamo il seguente codice:
private void mnuPersona_Click(object sender, System.EventArgs e) { frmPersona frm = new frmPersona(); frm.MdiParent = this; frm.Show(); }In questo modo, avviando l'applicazione e selezionando il menu Persona, si aprirà la nostra form vuota. Adesso andiamo ad inserire il codice per testare la nostra classe persona intercettando l'evento Form_Load della form, per fare questo, facciamo doppio click sulla form dal designer della form frmPersona e scriviamo il seguente codice:
private void frmPersona_Load(object sender, System.EventArgs e) { Persona p1 = new Persona( "Mario", "Rossi", "Via delle Magnolie, 25", "Milano", "21/01/1946"); Persona p2 = new Persona( "Giorgio", "Verdi" ); p2.Indirizzo = "Via delle camelie, 21"; p2.Citta = "Roma"; p2.DataNascita = "27/10/1958"; Persona p3 = new Persona(); p3.Nome = "Pietro"; p3.Cognome = "Bianchi"; p3.Indirizzo = "Via delle Begonie, 56"; p3.Citta = "Napoli"; p3.DataNascita = "26/07/1957"; this.lblPersona.Text = p1.ToString() + "\n"+ p2.ToString() + "\n" + p3.ToString(); }
In questo modo, instanziamo tre diverse classi persona e utilizziamo i 3 diversi costruttori e le proprietà pubbliche della classe per generare i signori Mario Rossi, Giorgio Verdi e Pietro Bianchi con i loro dati anagrafici. Il risultato dell'esecuzione della nostra form dal menu Persona sarà il seguente: Prima di concludere questa prima parte e passare ad implementare la nostra prima classe erede o figlia come preferiamo chiamarla, facciamo un passo indietro, verso i costruttori ed applichiamo una piccola ottimizzazione.
Il codice dei tre costruttori è il seguente:public Persona() { this.mNome = ""; this.mCognome = ""; this.mIndirizzo = ""; this.mCitta = ""; this.mDataNascita = ""; } public Persona(string pNome, string pCognome, string pIndirizzo, string pCitta, string pDataNascita) { this.mNome = pNome; this.mCognome = pCognome; this.mIndirizzo = pIndirizzo; this.mCitta = pCitta; this.mDataNascita = pDataNascita; } public Persona(string pNome, string pCognome) { this.mNome = pNome; this.mCognome = pCognome; this.mIndirizzo = ""; this.mCitta = ""; this.mDataNascita = ""; }Possiamo notare che le stesse istruzioni sono ripetute più volte. Ora, se si trattasse di un qualsiasi metodo di cui fare un «overload», potremmo scrivere il codice nel metodo con tutti i parametri e chiamare questo metodo dall'interno degli altri due, questo però per il costruttore non è possibile, ma naturalmente è possibile aggirare l'ostacolo.
Scriviamo un metodo che inizializza tutti e cinque i dati con i suoi cinque parametri e modifichiamo i costruttori per chiamarlo.private void InitPersona(string pNome, string pCognome, string pIndirizzo, string pCitta, string pDataNascita) { this.mNome = pNome; this.mCognome = pCognome; this.mIndirizzo = pIndirizzo; this.mCitta = pCitta; this.mDataNascita = pDataNascita; } public Persona() { InitPersona("","","","",""); } public Persona(string pNome, string pCognome) { InitPersona(pNome,pCognome,"","",""); } public Persona(string pNome, string pCognome, string pIndirizzo, string pCitta, string pDataNascita) { InitPersona(pNome,pCognome,pIndirizzo,pCitta,pDataNascita); }Perché una simile modifica? semplice, se dobbiamo modificare codice, modifichiamo una funzione e le altre saranno modificate di conseguenza.
L'ultima «dolente» nota
Per concludere davvero questa prima parte, inseriamo un ultima «dolente» nota: è appurato che i programmatori sono delle persone notevolmente pigre in tutto salvo che nella scrittura di codice, un programmatore potrà scrivere e far funzionare mille righe di codice in una giornata. Ma chiedete al programmatore di inserire all'interno del codice qualcosa che faccia capire a lui stesso o a un collega a cosa servono le sue classi, in modo che una correzione dopo un paio d'anni o una implementazione sia una cosa semplice da ottenere, ed è come se gli aveste inflitto una coltellata nella schiena o attaccato ai piedi una sfera da 100 chilogrammi e gli aveste detto di correre. Un mio collega ed amico risponderebbe «knowledge is power, quindi non commento il codice appositamente».
Purtroppo la realtà è diversa. A me è capitato anche recentemente di dover prendere in mano e ribaltare una funzione scritta circa quindici anni fa, la modifica è stata di due righe, ma per capire dove andavano messe, data l'assenza di commenti, è stata necessaria mezza giornata. Perciò, visto che Visual Studio .NET fornisce degli strumenti che vi permettono di scrivere commenti al vostro codice in modo velocissimo, attivate il Flag Smart Comment Editing nel Tab Formattazione della configurazione del Text Editor di C# e posizionatevi sulla riga precedente ad una dichiarazione di classe o a un metodo o a una proprietà digitando /// (tre barre) al posto delle classiche // (due) per i commenti C.
Otterrete qualcosa di spettacolare...
/// <summary> /// /// </summary> /// <param name="pNome"></param> /// <param name="pCognome"></param> /// <param name="pIndirizzo"></param> /// <param name="pCitta"></param> /// <param name="pDataNascita"></param>Questo è ciò che accade facendolo sulla riga prima della dichiarazione della funzione InitPersona, ed il cursore si posiziona sulla riga bianca sotto <summary> invitandovi a dare una descrizione della funzione a cui dovrete solo aggiungere una descrizione estesa dei suoi parametri che come potete notare sono compilati automaticamente.
Questi commenti racchiusi all'interno di tag XML possono essere poi automaticamente inseriti dal compilatore all'interno di un file XML, che, dato in pasto ad un Text formatter, vi potrà creare un manuale di documentazione completo.
Per attivare la creazione del file XML dei commenti basta andare sulle proprietà del progetto, aprire il ramo Build delle proprietà di configurazione ed inserire il nome del file che vogliamo creare nella riga Xml Documentation File.
Questo farà sì, per nostra «disgrazia», che il compilatore ci avvisi con un Warning di tutti i membri pubblici e tutte le classi che non hanno commenti XML, il che può forse essere una cosa fastidiosa all'inizio, ma se impariamo a utilizzare lo strumento correttamente da subito, diventerà routine e di conseguenza sarà poi facile non dimenticarsene e godere i frutti della piccola perdita di tempo in stesura del codice con un grande risparmio di tempo in revisione, modifica, ulteriore implementazione.Se poi come noi siete curiosi e date un'occhiata ad un Tool che si chiama Macros IDE, scoprirete che potete scrivervi delle funzioni in VBA non solo per creare commenti personalizzati all'interno dell'editor dell'IDE, ma soprattutto per scrivere codice in modo automatico e ad esempio crearvi delle macro per generare una classe collection o una classe dati di base secondo il vostro stile, che dovete solamente completare inserendo i nomi dei membri e le parti di codice peculiare risparmiandovi una quantità di tempo-tastiera.
Precedente Successiva