C# LE CLASSI - daniele.estesoft.itdaniele.estesoft.it/csharp/csharp-classi.pdf · Usando la...

15
1 C# LE CLASSI C# è un linguaggio orientato agli oggetti (O.O.P. = Object Oriented Programming). Un oggetto è la realizzazione in pratica di un qualcosa descritto in teoria . Esso ha delle caratteristiche, delle proprietà, degli attributi e dei metodi per utilizzarlo. Quando un architetto progetta una casa, ne fa il progetto su carta tramite un disegno che ne descrive le dimensioni, le caratteristiche ed eventualmente le tecniche di costruzione. Questo lavoro rappresenta la descrizione teorica di un oggetto (Casa) che dovrà essere costruito. In questo esempio, il progetto rappresenta la classe, mentre la casa realmente costruita , rappresenta l'oggetto vero e proprio. Usando la terminologia ad hoc, si dice che è stato istanziato l'oggetto casa cioè è stata creata una istanza della classe (progetto) Casa. In parole povere le classi sono definizioni usate per la creazione di oggetti. Dal progetto dell'architetto, volendo, si possono costruire più case. Per le classi, il concetto è lo stesso; da una classe si possono istanziare uno o più oggetti, dipende da quanti oggetti dello stesso tipo abbiamo bisogno in una applicazione. Facciamo un esempio: la classe Studente descrive tutte le caratteristiche di uno studente, per es. il nome, il cognome, la provenienza, ecc. . Con una istanza della classe Studente, che chiameremo Alunno, creeremo effettivamente un oggetto reale a partire della classe e che potremo usare nel nostro programma. E' ovvio che in una aula di una scuola c'è più di un alunno, quindi istanzieremo più classi Studente per formare la nostra aula di oggetti Alunno. Per saper utilizzare al meglio un linguaggio orientato agli oggetti, è necessario capire alcune caratteristiche molto importanti. Le caratteristiche principali di un linguaggio O.O.P. sono: l'incapsulamento, il polimorfismo, l'ereditarietà. L'incapsulamento è un concetto tramite il quale una classe consente di incorporare dati e tutti i metodi (funzioni) che li gestiscono. Ha la possibilità di nascondere questi elementi interni al mondo esterno. Le classi hanno dei meccanismi tali da rendere fruibili questi dati senza che l'utente sia a conoscenza di come sono strutturati. Nella maggior parte dei casi, è cosa positiva non rendere pubblici i dati di una classe perchè un utente (programmatore) deve solamente utilizzarla, non sapere cosa e come i dati sono stati impostati. Il codice del programma non sarà in grado di accedere ai dati della classe. Una classe avrà al suo interno dei metodi (li vedremo più avanti) per renderli disponibili. Cosi facendo, il programmatore della classe potrà modificare liberamente la struttura senza che il codice del programma ne subisca conseguenze. Il polimorfismo è la capacità di assumere molte forme. Nei programmini che abbiamo visto negli esempi, abbiamo fatto uso di un metodo per stampare sul monitor: Console.WriteLine(".....{0}",var); . L'argomento tra parentesi graffe {0}, si riferisce alla variabile var che dovrà essere stampata. Ma il metodo non sa di che tipo è la variabile che deve stampare. Il metodo in questione è polimorfico cioè si adatta alla maggior parte dei tipi che gli vengono passati. Lo stesso metodo può avere più di una variabile da stampare e lo fa tranquillamente appunto perchè è polimorfica. L'ereditarietà è il concetto un pò più complesso della programmazione orienteta agli oggetti. E' la possibilità di creare una classe che eredita le caratteristiche da un'altra classe espandendone le funzionalità . Per esempio possiamo creare una classe generale che chiameremo Autoveicoli che raggruppa tutti i veicoli che hanno in comune alcune proprietà e funzionalità. Possiamo creare una classe Autovettura che eredita le proprietà e funzionalità comuni e in più possiamo aggiungere proprietà e funzionalità tipiche di un'autovettura, per esempio marca, modello, motorizzazione ecc... . Abbiamo quindi creato una classe che ha ereditato le funzionalità dalla classe "genitore" e ne abbiamo espanso le funzionalità aggiungendone di nuove non presenti nella classe "genitore". L'ereditarietà è un concetto molto potente che ci consente di creare oggetti (classi) molto complessi e pieni di funzionalità. Avremo modo di parlarne in modo più approfondito quando creeremo classi personalizzate.

Transcript of C# LE CLASSI - daniele.estesoft.itdaniele.estesoft.it/csharp/csharp-classi.pdf · Usando la...

1

C# LE CLASSI C# è un linguaggio orientato agli oggetti (O.O.P. = Object Oriented Programming).

Un oggetto è la realizzazione in pratica di un qualcosa descritto in teoria. Esso ha delle caratteristiche, delle proprietà, degli attributi e dei metodi per utilizzarlo.

Quando un architetto progetta una casa, ne fa il progetto su carta tramite un disegno che ne descrive le dimensioni, le caratteristiche ed eventualmente le tecniche di costruzione.

Questo lavoro rappresenta la descrizione teorica di un oggetto (Casa) che dovrà essere costruito.

In questo esempio, il progetto rappresenta la classe, mentre la casa realmente costruita, rappresenta l'oggetto vero e proprio.

Usando la terminologia ad hoc, si dice che è stato istanziato l'oggetto casa cioè è stata creata una istanza della classe (progetto) Casa.

In parole povere le classi sono definizioni usate per la creazione di oggetti.

Dal progetto dell'architetto, volendo, si possono costruire più case. Per le classi, il concetto è lo stesso; da una classe si possono istanziare uno o più oggetti, dipende da quanti oggetti dello stesso tipo abbiamo bisogno in una applicazione.

Facciamo un esempio:

la classe Studente descrive tutte le caratteristiche di uno studente, per es. il nome, il cognome, la provenienza, ecc. . Con una istanza della classe Studente, che chiameremo Alunno, creeremo effettivamente un oggetto reale a partire della classe e che potremo usare nel nostro programma. E' ovvio che in una aula di una scuola c'è più di un alunno, quindi istanzieremo più classi Studente per formare la nostra aula di oggetti Alunno.

Per saper utilizzare al meglio un linguaggio orientato agli oggetti, è necessario capire alcune caratteristiche molto importanti.

Le caratteristiche principali di un linguaggio O.O.P. sono:

l'incapsulamento,

il polimorfismo,

l'ereditarietà.

L'incapsulamento è un concetto tramite il quale una classe consente di incorporare dati e tutti i metodi (funzioni) che li gestiscono. Ha la possibilità di nascondere questi elementi interni al mondo esterno. Le classi hanno dei meccanismi tali da rendere fruibili questi dati senza che l'utente sia a conoscenza di come sono strutturati. Nella maggior parte dei casi, è cosa positiva non rendere pubblici i dati di una classe perchè un utente (programmatore) deve solamente utilizzarla, non sapere cosa e come i dati sono stati impostati. Il codice del programma non sarà in grado di accedere ai dati della classe. Una classe avrà al suo interno dei metodi (li vedremo più avanti) per renderli disponibili. Cosi facendo, il programmatore della classe potrà modificare liberamente la struttura senza che il codice del programma ne subisca conseguenze.

Il polimorfismo è la capacità di assumere molte forme. Nei programmini che abbiamo visto negli esempi, abbiamo fatto uso di un metodo per stampare sul monitor: Console.WriteLine(".....{0}",var); . L'argomento tra parentesi graffe {0}, si riferisce alla variabile var che dovrà essere stampata. Ma il metodo non sa di che tipo è la variabile che deve stampare. Il metodo in questione è polimorfico cioè si adatta alla maggior parte dei tipi che gli vengono passati. Lo stesso metodo può avere più di una variabile da stampare e lo fa tranquillamente appunto perchè è polimorfica.

L'ereditarietà è il concetto un pò più complesso della programmazione orienteta agli oggetti. E' la possibilità di creare una classe che eredita le caratteristiche da un'altra classe espandendone le funzionalità. Per esempio possiamo creare una classe generale che chiameremo Autoveicoli che raggruppa tutti i veicoli che hanno in comune alcune proprietà e funzionalità. Possiamo creare una classe Autovettura che eredita le proprietà e funzionalità comuni e in più possiamo aggiungere proprietà e funzionalità tipiche di un'autovettura, per esempio marca, modello, motorizzazione ecc... . Abbiamo quindi creato una classe che ha ereditato le funzionalità dalla classe "genitore" e ne abbiamo espanso le funzionalità aggiungendone di nuove non presenti nella classe "genitore". L'ereditarietà è un concetto molto potente che ci consente di creare oggetti (classi) molto complessi e pieni di funzionalità. Avremo modo di parlarne in modo più approfondito quando creeremo classi personalizzate.

2

Non preoccupatevi se avete incontrato qualche difficoltà nell'apprendere questi concetti, è normale, man mano che andremo avanti si chiariranno.

Definizione di una classe.

Per definire una classe, abbiamo bisogno di una intestazione rappresentata dalla parola chiave class e da un corpo che ne racchiude il contenuto, così:

class NomeClasse

{

elementi definiti nel corpo

}

dove il NomeClasse o identificatore rappresenta il nome che vogliamo dare alla classe. E’ utile dare un nome significativo alla classe che ne descriva l’impiego. Ad esempio possiamo definire la classe Studente:

class Studente

{

elementi definiti nel corpo

}

dal suo identificatore (Studente), capiamo al volo che si tratta di qualcosa che a che fare con una persona; un altro esempio:

class Autovettura

{

elementi definiti nel corpo

}

anche in questo caso si capisce, dall’identificatore (Autovettura), che avremo a che fare con un veicolo.

Una volta creata una classe, non possiamo utilizzarla senza dichiararla. Siamo di fronte ad un tipo particolare di dato che noi abbiamo realizzato (progetto) e da questo dobbiamo creare un oggetto che lo rappresenti e che possiamo utilizzare. La dichiarazione (istanza) di una classe avviene in maniera quasi banale, cioè faremo così:

NomeClasse nomeOggetto = new NomeClasse();

ritornando agli esempi che ho fatto prima, possiamo creare (istanziare) un oggetto Studente che chiameremo alunno così:

Studente alunno = new Studente ();

allo stesso modo per un oggetto Autovettura che chiameremo miaMacchina:

Autovettura miaMacchina = new Autovettura ();

abbiamo creato due oggetti, alunno di tipo Studente e miaMacchina di tipo Autovettura.

Ma questi oggetti non servono a molto in una applicazione, sono oggetti "vuoti", cioè non contengono al loro interno qualcosa che li faccia distinguere tra loro. Per esempio un alunno avrà pure un nome ed un cognome, la miaMacchina avrà pure una cilindrata, una marca, un colore, ecc. . Ebbene, per avere queste informazioni, dobbiamo necessariamente inserire all’interno del loro corpo delle variabili che ci indicano tali informazioni, o per meglio dire, tali proprietà.

Tali variabili vengono definite, in gergo tecnico, variabili istanza o campi istanza, ed essendo definite a “livello classe”, e quindi visibili a tutti i metodi della classe vengono dichiarate per convenzione con la lettera iniziale maiuscola, per contraddistinguerle dalle variabili locali utilizzate all’interno dei metodi.

Facciamo subito un esempio di come vengono dichiarati i campi istanza. Prendiamo la classe Studente che qui ripropongo:

class Studente

{

elementi definiti nel corpo

}

3

senza ombra di dubbio possiamo affermare che uno studente ha un nome ed un cognome che potremmo rappresentare tramite due variabili di tipo stringa. Ecco come:

class Studente

{

// definisco due campi istanza

string Nome;

string Cognome;

}

Ora la classe ha un aspetto più concreto ma non ancora utilizzabile. Così com’è, pur essendo una classe a tutti gli effetti, se istanziamo un oggetto Alunno, non potremo mai sapere come si chiama. I suoi campi istanza non saranno fruibili dal codice esterno perché così come sono stati dichiarati diventano automaticamente privati (in gergo private) alla classe e nessun codice può accedervi. Qui entrano in gioco i modificatori di accesso. Dobbiamo fare in modo che questi campi istanza diventino pubblici, cioè accessibili a tutto il codice utente.

Questa è la nuova classe:

class Studente

{

// definisco due campi istanza

public string Nome;

public string Cognome;

}

Finalmente ora la nostra classe è pronta per l’uso. Abbiamo inserito il modificatore public con l’intenzione di utilizzare i campi istanza all’esterno della classe e quindi dal nostro programma. Come vedremo più avanti, i campi istanza con modificatore private potranno essere comunque manipolati tramite un costrutto molto semplice. Però adesso concentriamoci sulla nostra classe e vediamo come valorizzare questi campi.

Per prima cosa dovremo istanziare un oggetto dalla classe tramite il quale accederemo ai campi istanza che abbiamo creato, quindi:

Studente alunno = new Studente();

Per accedere ai campi istanza faremo uso di una particolare notazione, la notazione puntata in questo modo:

alunno.nome = "Mario";

con questa istruzione, abbiamo assegnato al campo istanza Nome dell’oggetto alunno la stringa "Mario".

Allo stesso modo faremo per il cognome:

alunno.cognome = "Rossi";

All’assegnazione di un valore ad una variabile, ne corrisponde anche una lettura e nel nostro caso faremo l’operazione inversa; se avessimo una variabile stringa chiamata per esempio datoNome, potremo valorizzarla leggendo il valore memorizzato nel campo istanza del nostro oggetto:

datoNome = alunno.nome;

i campi istanza sono delle normali variabili e quindi le possiamo manipolare come sappiamo.

4

A questo punto della trattazione, non ci resta che scrivere qualche riga di codice per vedere in azione la nostra classe:

// questo programma usa la classe Studente

using System;

class Studente // la classe

{

// campi istanza della classe

public string Nome;

public string Cognome;

}

class Alunno // il programma

{

public static void Main()

{

// istanzio la classe

Studente alunno = new Studente();

// valorizzo i campi dell’oggetto

alunno.Nome = "Mario";

alunno.Cognome = "Rossi";

// stampo i valori dei campi

Console.WriteLine("\nIl nome dell’alunno è {0} {1}",

alunno.Nome,

alunno.Cognome);

// attendo un INVIO per chiudere il programma

Console.ReadLine();

}

}

Copiate e compilate il codice.

Questo è il risultato:

il nome dell’alunno è Mario Rossi

Il programma appena scritto, pur non avendo nessuna utilità pratica se non quella di stampare delle stringhe, ci consente di capire come utilizzare una classe all’interno di una applicazione. Per prima cosa, notiamo dove è posizionata la classe all’interno del codice; viene posizionata subito dopo la direttiva (o le direttive) using e prima della dichiarazione della classe programma. All’interno del metodo Main(), che rappresenta il punto di inizio del programma, troviamo l’istruzione che istanzia la classe (riga 17) e quindi crea l’oggetto alunno sul quale faremo le nostre operazioni. Le istruzioni delle righe 20 e 21 valorizzano rispettivamente il campo nome ed il campo cognome dell’oggetto. Infine alla riga 24 troviamo l’istruzione che effettua la stampa con una sintassi del tutto corretta.

Nella prossima lezione, approfondiremo altri concetti molto importanti sulle classi come i metodi e le proprietà

Nelle classi di esempio utilizzate nella precedente lezione, abbiamo utilizzato dei campi istanza (variabili della classe) che avevano il modificatore di accesso public con il quale il codice esterno alla classe poteva accedere direttamente ai loro valori.

Nella normale programmazione orientata agli oggetti, è consigliabile garantire una maggiore "ristrettezza" riguardo l’accesso ai campi istanza della classe. Con ristrettezza si intende cioè "mascherare" la natura e la struttura dei campi inclusi nella classe. L’utilizzatore della classe non deve interessarsi di come sono stati creati e/o manipolati i campi istanza dell’oggetto che dovrà gestire all’interno di una applicazione, ma li dovrà solamente utilizzare.

I progettisti di una classe, nascondono, rendendoli privati, tutti (o quasi) i campi istanza di una classe.

La domanda nasce spontanea: "Se i campi vengono dichiarati privati (private), come si possono utilizzare all’interno di una applicazione se non possiamo accedervi?". La risposta è nel capitolo seguente.

5

LE PROPRIETA’

Il C# mette a disposizione un costrutto particolare creato ad hoc per poter accedere indirettamente ai campi istanza di una classe: le proprietà.

Con le proprietà, siamo in grado di accedere ai campi istanza sia in lettura che in scrittura; questa è la sintassi:

public tipo nome_proprietà

{

get

{

return nome_campo;

}

set

{

nome_campo = value;

}

}

La segnatura indica che la proprietà è public, quindi accessibile dal codice esterno alla classe, è dichiarata di un tipo tra quelli consentiti dal linguaggio e che è in stretta relazione con quello del campo istanza a cui farà riferimento ed infine ha un nome.

Le parole chiave get e set consentono l’effettivo accesso al campo istanza ed in particolare con:

get

Saremo in grado di leggere il contenuto del campo. Nel suo blocco di codice noterete la parola chiave return. Essa ci ritorna il valore del campo nome_campo (tra breve vedremo come).

Questo consente al programmatore di valutare e decidere se necessario quale valore restituire, alterando il risultato a seconda di determinate condizioni nel corso dell’esecuzione del programma.

set

Saremo in grado di assegnare un preciso valore al campo nome_campo tramite la parola chiave value.

Questo consente al programmatore di controllare il valore da assegnare, evitando di utilizzare valori errati che comprometterebbero il funzionamento della classe stessa.

Le proprietà possono contenere entrambe le sezioni (get/set) o solo una di esse, rendendole a “sola lettura” o “sola scrittura”. Per convenzione i nomi dei campi istanza che si riferiscono ad una proprietà vengono preceduti dal prefisso Var.

NOTA: a seconda delle esigenze le proprietà possono non memorizzare il loro valore in una variabile, questo è molto frequente quando il loro valore è dipendente da quello di altre proprietà.

Come al solito la teoria deve necessariamente lasciare spazio alla pratica che svelerà molto semplicemente i "retroscena" dell’uso delle proprietà.

Riprendiamo la classe Studente della lezione precedente e che qui riporto:

class Studente

{

// campi istanza della classe

public string Nome;

public string Cognome;

}

6

Ora la trasformeremo aggiungendovi tre proprietà:

Nome, con riferimento al campo istanza/variabile VarNome in quanto memorizza il valore in VarNome

Cognome, con riferimento al campo istanza/variabile VarCognome in quanto memorizza il valore in VarCognome

NomeCompleto, senza riferimenti a campi istanza/variabili in quanto il suo valore è dipendente da Nome e Cognome

Per prima cosa eliminiamo il modificatore public rendendo entrambi i campi privati. Ricordo che se ad un campo non diamo nessun modificatore di accesso, il campo diventa automaticamente private. Ora la classe si presenta così:

class Studente

{

// campi istanza privati della classe

string VarNome;

string VarCognome;

}

Inseriamo le proprietà descritte sopra:

class Studente

{

// campo istanza privato della classe

string VarNome;

// definisco la proprietà per il campo “Nome”

public string Nome // è dello stesso tipo del campo istanza

{

get // per la lettura

{

return VarNome; // ritorna il valore stringa del campo

}

set // per l’assegnazione

{

VarNome = value; // assegno un valore stringa al campo

}

}

// campo istanza privato della classe

string VarCognome;

// definisco la proprietà per il campo “Cognome”

public string Cognome // è dello stesso tipo del campo istanza

{

get // per la lettura

{

return VarCognome; // ritorna il valore stringa del campo

}

set // per l’assegnazione

{

VarCognome = value; // assegno un valore stringa al campo

}

}

// definisco la proprietà per il campo “NomeCompleto”

public string NomeCompleto

{

get // per la SOLA lettura

{

return VarCognome + “ “ + VarNome; // ritorna il nome completo

}

}

} // fine blocco di codice della classe

7

La classe è terminata ed è pronta all’uso. Ma ora che i campi istanza sono "spariti", in che modo le proprietà verranno utilizzate nel codice ?

Le proprietà, come avete imparato, hanno un nome ed è proprio tramite il nome che accederemo ai campi. Le proprietà possono essere considerate come semplici variabili e come tali verranno usate nel codice.

Normalmente quando assegniamo un valore ad una variabile siamo soliti scrivere, per esempio:

numero = 124;

oppure se vogliamo leggerne il contenuto scriviamo:

num_libri = numero;

con le proprietà faremo la stessa cosa ma con la variante che, avendo a che fare con un oggetto, dobbiamo utilizzare la notazione puntata (operatore punto).

Per esempio se abbiamo istanziato un oggetto alunno tramite la classe Studente e vogliamo assegnargli un nome ed un cognome faremo così:

// 1: per il nome

alunno.Nome = "Valentino";

// 2: per il cognome

alunno.Cognome = "Rossi";

// 3: per il nome completo ERRORE!

alunno.NomeCompleto = "Rossi";

le istruzioni, hanno effettuato una assegnazione di un valore per i campi privati della classe richiamando automaticamente il costrutto set attraverso le rispettive proprietà (Nome e Cognome).

Il terzo esempio, quello relativo al “NomeCompleto” genererebbe un errore in quanto la proprietà NomeCompleto non è dotata di costrutto set, risultando di sola lettura.

Al contrario, se vogliamo leggere i valori contenuti nei campi privati della classe sarà sufficiente inserire l’accesso alla proprietà nel punto del codice in cui serve il rispettivo valore.

L’esempio seguente visualizza a video il valore di ciascuna delle tre proprietà:

// 1: per il nome

Console.WriteLine(alunno.Nome);

// 2: per il cognome

Console.WriteLine(alunno.Cognome);

// 3: per il nome completo

Console.WriteLine(alunno.NomeCompleto);

le istruzioni richiamano automaticamente il costrutto get delle rispettive proprietà che:

nel caso di Nome e Cognome: ci ritornerà il valore del campo privato associato tramite la parola chiave return

nel caso di NomeCompleto: ci ritornerà il risultato del calcolo effettuato nel costrutto get, tramite la parola chiave return

8

E’ arrivato il momento di vedere in azione la nuova classe con questo semplice programmino. Copiate il listato, compilatelo e lanciatelo.

// questo programma usa la classe Studente modificata

using System;

class Studente

{

// campo istanza privato della classe

string VarNome;

// definisco la proprietà per il campo “Nome”

public string Nome // è dello stesso tipo del campo istanza

{

get // per la lettura

{

return VarNome; // ritorna il valore stringa del campo

}

set // per l’assegnazione

{

VarNome = value; // assegno un valore stringa al campo

}

}

// campo istanza privato della classe

string VarCognome;

// definisco la proprietà per il campo “Cognome”

public string Cognome // è dello stesso tipo del campo istanza

{

get // per la lettura

{

return VarCognome; // ritorna il valore stringa del campo

}

set // per l’assegnazione

{

VarCognome = value; // assegno un valore stringa al campo

}

}

// definisco la proprietà per il campo “NomeCompleto”

public string NomeCompleto

{

get // per la SOLA lettura

{

return VarCognome + “ “ + VarNome; // ritorna il nome completo

}

}

} // fine blocco di codice della classe

class Program // il programma

{

public static void Main()

{

// istanzio la classe

Studente Alunno = new Studente();

// valorizzo le proprietà dell’oggetto

Alunno.st_nome = "Valentino";

Alunno.st_cognome = "Rossi";

// stampo i valori dei campi

Console.WriteLine("\nIl nome dell’alunno è {0} {1}",

Alunno.st_nome, Alunno.st_cognome);

// attendo un carattere qualsiasi

// digitato per chiudere il programma

Console.ReadLine();

} // chiude il Main()

} // chiude il programma

9

I METODI

Una classe può disporre oltre che di campi istanza e proprietà anche di metodi, intesi come funzionalità destinate a risolvere determinati problemi attinenti la classe.

Un metodo può:

contenere più di una istruzione, permettendo l’esecuzione di azioni più complesse di quelle risolvibili mediante una proprietà

ricevere uno o più parametri, intesi come variabili locali con ambito di visibilità legato al metodo stesso, da utilizzare per la risoluzione del problema

restituire un solo risultato mediante un valore di ritorno, o più risultati mediante più parametri passati per riferimento

DICHIARAZIONE

Per dichiarare un metodo occorre specificare:

ambito_visibilità tipo_risultato nome_metodo ([ref]tipo_param1 nome_param1, ...)

{

// istruzioni da compiere ...

}

L’ambito di visibilità può essere uno di quelli conosciuti (public, private, …).

Il tipo di risultato può essere un qualsiasi tipo primitivo (int, double, string, ….) o classe. Deve corrispondere alla tipologia di dato che vogliamo far restituire al metodo. Se il metodo non restituisce nulla, deve corrispondere alla parola chiave VOID.

Il nome del metodo deve soddisfare i criteri visti per variabili, proprietà e campi di istanza.

I parametri non sono indispensabili, ma nel caso siano utilizzati occorre:

o Specificare per ognuno il tipo e il nome

o Nel caso debbano essere utilizzati anche per restituire un valore occorre anteporre al tipo la parola chiave REF. In questo caso il parametro non costituisce una copia del valore passato, ma un riferimento diretto alla variabile passata al momento della chiamata, che al termine dell’esecuzione del metodo risulterà influenzata dai cambiamenti apportati nel metodo.

o Separarli da una virgola nel caso siano più di uno

NOTA: All’interno della stessa classe possono coesistere metodi con lo stesso nome, ma si devono necessariamente differenziare per il NUMERO di parametri o per il TIPO. Questa possibilità viene chiamata OVERLOADING, cioè sovraccarico, è possibile definire metodi con lo stesso nome, che risolvono lo stesso problema, che utilizzano parametri in ingresso differenti.

Nell’esempio seguente viene implementato un metodo cercando di riassumere tutte le tipologie possibili.

La classe Articolo dispone di due campi di istanza pubblici Prezzo e Sconto, vengono implementati alcuni metodi per il calcolo del prezzo scontato.

La classe Program istanzia un oggetto di tipo Articolo e ne utilizza i relativi metodi.

10

class Articolo

{

// Dichiarazione campi di istanza

public double Sconto, Prezzo;

// 1: Il metodo esegue il calcolo utilizzando i campi istanza

// e visualizza il risultato direttamente sulla console

public void PrezzoScontato()

{

Console.WriteLine(Prezzo * (100 – Sconto) / 100);

}

// 2: Il metodo esegue il calcolo utilizzando i campi istanza

// e RESTITUISCE il risultato alla funzione chiamante

// NOTA: aggiungo R al nome per differenziarlo dal precedente in quanto

// entrambi non dispongono di parametri

public double PrezzoScontatoR()

{

return (Prezzo * (100 – Sconto) / 100);

}

// 3: Il metodo esegue il calcolo utilizzando i parametri forniti

// e visualizza il risultato direttamente sulla console

public void PrezzoScontato(double aPrezzo, double aSconto)

{

Console.WriteLine(aPrezzo * (100 – aSconto) / 100);

}

// 4: Il metodo esegue il calcolo utilizzando i parametri forniti

// e RESTITUISCE il risultato alla funzione chiamante

// NOTA: aggiungo R al nome per differenziarlo dal precedente in quanto

// entrambi dispongono dello stesso numero (2) e tipo di parametri (double)

public double PrezzoScontatoR(double aPrezzo, double aSconto)

{

return (aPrezzo * (100 – aSconto) / 100);

}

// 5: Il metodo esegue il calcolo utilizzando i campi istanza

// e RESTITUISCE il risultato alla funzione chiamante mediante un parametro

// passato per RIFERIMENTO (REFerence)

public void PrezzoScontato(ref double aPrezzoScontato)

{

// Impone la modifica alla variabile passata per indirizzo

aPrezzoScontato = (Prezzo * (100 – Sconto) / 100);

}

}

class Program

{

public static void Main()

{

// Istanzia un oggetto di tipo Articolo

Articolo art = new Articolo();

// Visualizza il prezzo scontato (metodo1)

art.PrezzoScontato();

// Visualizza il prezzo scontato (metodo2)

Console.WriteLine(art.PrezzoScontatoR());

// Visualizza il prezzo scontato (metodo3)

art.PrezzoScontato(1000, 10);

// Visualizza il prezzo scontato (metodo4)

Console.WriteLine(art.PrezzoScontatoR(1000, 10));

// Visualizza il prezzo scontato (metodo5)

double risultato = 0;

art.PrezzoScontato(ref risultato); // passaggio per riferimento

Console.WriteLine(risultato); // visualizza modifica apportata nel metodo

}

}

11

I METODI COSTRUTTORI

Ogni volta che viene istanziato un oggetto con la parola chiave new il frame work ricerca all’interno della classe il metodo utilizzato per la sua costruzione, tale metodo viene detto costruttore.

Il framework non impone la definizione di un costruttore per ogni classe definita dal programmatore, lasciando un altissimo grado di personalizzazione. Per ogni classe viene considerata l’esistenza di un costruttore predefinito, che anche se non esplicitamente definito, può essere utilizzato per istanziare la classe. Il metodo costruttore, essendo chiamato in fase di creazione dell’istanza, può essere utilizzato per compiere le operazioni iniziali relative alla classe, ad esempio l’inizializzazione di alcune proprietà o campi di istanza.

E’ possibile sottoporre i costruttori ad overload, come visto per i metodi normali, in questo modo si offre la possibilità di creare un’istanza della classe utilizzando parametri diversi in fase di costruzione.

DICHIARAZIONE

public nome_classe ([ref]tipo_param1 nome_param1, ...)

[:this(...)]

{

// istruzioni da compiere ...

}

L’ambito di visibilità deve essere necessariamente public, per dare la possibilità di chiamare il costruttore dalla classe utilizzatrice (es. applicazione).

Il nome del metodo DEVE essere necessariamente uguale a quello della classe.

Il costruttore può avere o meno parametri, ma bisogna tenere conto di una cosa importante:

se si definisce un costruttore con parametri si perde automaticamente la possibilità di utilizzare il costruttore predefinito, che se si ha la necessità di utilizzare andrà definito esplicitamente all’interno della classe.

Se all’interno della classe sono dichiarati più costruttori è possibile chiamarne uno all’interno della definizione di un altro, in modo da eseguire quelle che sono le istruzione previste dal metodo a cui si fa riferimento [:this(…)]

Nell’esempio seguente vengono dichiarati più costruttori, in modo da offrire diverse possibilità per istanziare un nuovo oggetto di tipo “Articolo”:

class Articolo

{

// Dichiarazione campi di istanza

public double Sconto, Prezzo;

// 1: Costruttore vuoto

// Inizializza i campi di istanza ai valori iniziali

public Articolo ()

{

Sconto = 0;

Prezzo = 0;

}

// 2: Costruttore con parametri

// Inizializza i campi di istanza ai valori indicati dai parametri

public Articolo (double aSconto, double aPrezzo)

{

Sconto = aSconto;

Prezzo = aPrezzo;

}

// 3: Costruttore con un solo parametro

// Inizializza i campi di istanza ai valori iniziali mediante il richiamo del metodo

// costruttore 1 e varia il campo Prezzo con quello indicato dal parametro aPrezzo

public Articolo (double aPrezzo)

: this()

{

Sconto = aSconto;

}

}

12

GLI EVENTI

C# ha introdotto una semplificazione sostanziale della gestione degli eventi nella programmazione, rendendo il programmatore libero di inventare e definire eventi personalizzati all’occorenza.

Per evento si intende il verificarsi di una condizione particolare all’interno di una classe durante la normale esecuzione del programma, alla quale associare una notifica da eseguire nella classe utilizzatrice (es. programma).

Un esempio banale potrebbe essere il livello di scorta di un articolo di magazzino, al raggiungimento del livello di riordino impone all’applicazione di eseguire una procedura specifica (vedi esempio più avanti).

Ad ogni evento è associato un Handler (gestore), che consiste in una tipologia specifica di metodo utilizzato per gestirlo e riceverlo.

C# mette a disposizione molti Handler, a seconda dell’evento da “catturare”, ma per eventi standard esiste un handler generico di nome EventHandler. Altri gestori sono per esempio:

KeyEventHandler che gestisce la pressione dei tasti sulla tastiera

MouseEventHandler che gestisce i movimenti del mouse

Ogni handler è poi associato ad una istanza del tipo EventArgs che costituisce elementi addizionali comunicati insieme alla notifica dell’evento. Nei due esempi precedenti sono:

KeyEventArgs che comunica il codice ascii del tasto premuto, e la notifica se eventuali tasti funzione sono premuti in contemporanea (SHIFT, ALT, CTRL)

MouseEventArgs che comunica quale pulsante del mouse è stato premuto, in quale posizione e quante volte

Durante la comunicazione dell’evento viene sempre comunicato:

l’istanza che ha generato l’evento

gli elementi addizionali (EventArgs)

DEFINIZIONE (nella classe che mette a disposizione e genera l’evento):

Per definire un evento all’interno di una classe è sufficiente indicare:

accessibilità event tipo_handler nome_evento;

DEFINIZIONE (nella classe che riceve la notifica dell’evento):

La classe utilizzatrice deve dichiarare un metodo che rispetti la tipologia di Handler utilizzata:

// aSender: istanza che ha eseguito la notifica

// aArgomenti: elementi addizionali

void nome_metodo (object aSender, event_args aArgomenti)

{

// istruzioni da compiere

}

UTILIZZO (nella classe che mette a disposizione e genera l’evento):

Per generare l’evento sarà sufficiente assicurarsi che vi sia stata associata un’azione e di conseguenza richiamare la procedura:

if (nome_evento != null)

{

// this = questa istanza sta notificando l’evento

// event_args = elementi addizionali

Nome_evento(this, event_args);

}

13

UTILIZZO (nella classe che riceve la notifica dell’evento):

La classe utilizzatrice deve associare una procedura all’evento dell’istanza da controllare:

nome_istanza.nome_evento += new tipo_handler(nome_procedura);

Implementiamo l’esempio proposto precedentemente dell’articolo di magazzino:

class Articolo

{

// Dichiarazione dell’evento di raggiungimento del livello di riordino

public event EventHandler Riordino;

// Dichiarazione di un membro dati pubblico con il nome

public string Nome;

// Dichiarazione di una proprietà Kg (chilogrammi presenti in magazzino)

private int VarKg = 1000;

public int Kg

{

get

{

return VarKg;

}

set

{

// Impostazione nuovo valore

VarKg = value;

// Se scende sotto i 100Kg genera l’evento di allarme

if (VarKg < 100 && Riordino != null)

Riordino(this, new EventArgs());

}

}

}

class Program

{

public static void Main()

{

// Istanzia un oggetto di tipo Articolo

Articolo art = new Articolo();

// Associa all’evento messo a disposizione da Articolo la procedura Allarme

art.Riordino += new EventHandler(Allarme);

// Modifica Nome

art.Nome = “Banane”;

// Modifica la proprietà Kg, indicando un valore inferiore a 100

// provocando la generazione dell’evento

art.Kg = 50;

}

// Metodo chiamato SOLO in caso di notifica dell’evento

// aSender: istanza (oggetto di tipo Articolo) che l’ha notificato

// aArgomenti: argomenti addizionali

public static void Allarme(objet aSender, EventArgs aArgomenti)

{

// Output

Console.WriteLine(“ATTENZIONE!!! Raggiunto punto di riordino!”);

// Dal momento che aSender rappresenta un’istanza di tipo “Articolo”

// è possibile, tramite un cast, accedere a tutti gli elementi pubblici

// dell’istanza

Console.WriteLine(“Articolo: “ + ((Articolo)aSender).Nome);

Console.WriteLine(“Quantità: “ + ((Articolo)aSender).Kg);

}

}

14

LA PAROLA CHIAVE STATIC

L’istruzione riservata static consente di dichiarare una classe o un elemento all’interno di una classe, visibile e condiviso da tutte le future istanza di quella classe. E’ possibile dichiarare i seguenti elementi con questo modificatore:

classe Permette l’utilizzo di tutte le proprietà e i metodi in essa definiti senza la necessità di istanziare un oggetto dello stesso tipo. Utile per riassumere in una classe metodi o proprietà di uso comune, in modo da poterle utilizzare velocemente raggruppate per funzionalità

variabili/campi istanza La modifica apportata ad una variabile statica è riflessa su tutte le istanze

proprietà La modifica apportata ad una proprietà statica è riflessa su tutte le istanze. Nei costrutti get/set è possibile utilizzare SOLO variabili e metodi a livello classe di tipo STATIC

eventi L’evento è utilizzabile a livello di classe, e quindi generabile da parte di tutte le istanze Nell’implementazione dell’evento è possibile utilizzare SOLO variabili e metodi a livello classe di tipo STATIC

metodi Il metodo è utilizzabile in qualsiasi momento, da tutte le istanze o dalla classe utilizzatrice (es. il programma) Nell’implementazione del metodo è possibile utilizzare SOLO variabili e metodi a livello classe di tipo STATIC

DICHIARAZIONE:

Per dichiarare un elemento statico è sufficiente far seguire al suo ambito di visibilità la parola STATIC.

class Scontrino

{

// Dichiarazione di una proprietà STATICA Aliquota condivisa da tutte le istanze

private static int VarAliquota;

public static int Aliquota

{

get { return VarAliquota; }

set { VarAliquota = value; }

}

// Dichiarazione di una proprietà Prezzo

private double VarPrezzo;

public double Prezzo

{

get { return VarPrezzo; }

set { VarPrezzo = value; }

}

// Dichiarazione di una proprietà Totale

// che restituisce il prezzo ivato

public double Totale

{

get { return VarPrezzo * (100 + Aliquota) / 100; }

}

// Dichiarazione di un metodo STATICO TotaleIvato utilizzabile in qualsiasi momento

// che restituisce il prezzo ivato

public static double TotaleIvato (double aPrezzo)

{

return aPrezzo * (100 + Aliquota) / 100;

}

}

Nell’esempio precedente:

La proprietà Aliquota deve utilizzare come variabile private una variabile statica, in quanto la proprietà è definita static.

15

La proprietà Totale restituirà un valore differente per ciascuna istanza di tipo Scontrino a seconda del valore della proprietà Prezzo. Modificando il valore della proprietà Aliquota si otterrà come conseguenza l’applicazione della nuova aliquota iva per tutte le istanze di scontrino.

Il metodo TotaleIvato può essere utilizzato in qualsiasi momento per la determinazione dell’importo totale di un prezzo qualsiasi applicando l’aliquota iva corrente. Il metodo non può usare la proprietà Prezzo in quanto esso è dichiarato static, e può utilizzare al suo interno solo variabili, proprietà o metodi statici della classe.

UTILIZZO:

Per richiamare un elemento statico di una classe è sufficiente anteporre al nome dell’elemento interessato il nome della classe seguita dal punto; come per l’utilizzo di una normale proprietà o metodo, ma sostituendo il nome dell’istanza con quello della classe.

Considerando la classe Scontrino vista sopra, guardiamo il prossimo esempio:

class Program { // Impostazione dell’aliquota da utilizzare Scontrino.Aliquota = 20; // Accesso e modifica della proprietà STATIC // Creazione di una prima istanza Scontrino Scontrino sc1 = new Scontrino(); sc1.Prezzo = 100; // Creazione di una seconda istanza Scontrino Scontrino sc2 = new Scontrino(); sc2.Prezzo = 1000; // Visualizzazione dell’importo totale utilizzando la proprietà Totale // NOTA: viene applicata l’aliquota del 20% ad entrambi gli importi, in quanto comune alla classe Console.WriteLine(sc1.Totale); Console.WriteLine(sc2.Totale); // Visualizzazione dell’importo totale utilizzando il metodo TotaleIvato // NOTA: viene applicata l’aliquota del 20% ad entrambi gli importi, in quanto comune alla classe Console.WriteLine(Scontrino.TotaleIvato(sc1.Prezzo); // Accesso al metodo STATIC Console.WriteLine(Scontrino.TotaleIvato(sc2.Prezzo); // Accesso al metodo STATIC }