E' arrivato il momento di entrare nel vivo della programmazione C# descrivendo prima, ed usando poi, un concetto che è alla base di questo linguaggio (ed altri linguaggi moderni):

le classi.

All'inizio di questo corso, ho detto che il 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.

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.

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

class Studente
{
    elementi definiti nel corpo
}

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.

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

N.R.

Codice sorgente Alunno.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

// 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 carattere qualsiasi
              // digitato per chiudere il programma 
            Console.ReadLine();
          }
      } 
            

Copiate e compilate il codice.

Questo è il risultato:

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à.

Pagina precedente - Pagina successiva