C#

LE CLASSI - get e set

Il titolo di questo paragrafo ci presenta due utili keyword che potremo usare e incontreremo spesso, nell'uso delle classi.

set - serve per attribuire un valore all'elemento sul quale è applicato
get - restituisce il valore dell'elemento al quale è applicato

Per quanto riguarda le classi la cosa è molto semplice da un punto di vista pratico mentre è importante teoricamente in quanto le keyword in parola  ci permettono di introdurre le proprietà le quali a loro volta realizzano nella pratica il concetto di incapsulamento. Quest'ultimo, detto in parole molto semplici, è la capacità di un oggetto di contenere, manipolare e proteggere, le proprie caratteristiche in maniera autonoma potendo evitare di esporre parti non condivisibili. L'incapsulamento viene gestito in molti altri linguaggi per i quali rappresenta una caratteristica moderna ed importante tanto da essere considerata uno dei pilastri della programmazione OO. Come stiamo per vedere set e get ci offrono alcune possibilità molto comode.
In C# le proprietà viste esternamente rispetto alla classe, somigliano in tutto e per tutto a dei campi ma internamente può essere sviluppata al loro interno una logica procedurale. Non per nulla vengono chiamate a volte "smart fields". L'esempio seguente è un po' la base di tutto il discorso:

  Esempio 22.1
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
using System;

class Numeri
{
  private static int x;
  public int X
  {
    get
    {
      return x;
    }
    set
    {
       x = value;
    }
  }
}

class Test
{
  public static void Main()
  {
    Numeri x1 = new Numeri();
    x1.X = 3;
    Console.WriteLine(x1.X);
  }
}

Dalla riga 6 alla 16 abbiamo quel che ci interessa. La proprietà è geralmente espressa con un riferimento al campo sul quale agisce anche se sintatticamente non è obbliagatorio, in questo caso il valore che viene trattato è x e ad esso ci riferiamo con la proprietà X, si tratta di un accorgimento che aiuta la lettura del codice. Come è facilmente leggibile, get si occupa di restituire il valore di x mentre set attribuisce alla stessa variabile il valore che può essere passato ad essa, cosa che avviene alla riga 24. Alla 25 invece, viene richiamato get in quanto [ richiesto un valore in uscita. Come si vede tutto viene gestito senza toccare, dall'esterno la nostra x. Da notare la presenza della parola value che funziona, in pratica, da parametro di input che trasporta il valore da assegnare. Non si tratta di una keyword in senso stretto ma una parola contestuale in quanto legata ad un preciso ambito di utilizzo.
Ovviamente è possibile operare in maniera più complessa nell'ambito di set e get. Anche qui, ecco un esempio di base:

  Esempio 22.2
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
31
32
33
34
35
using System;

class TC
{
  public int x1;
  private string s1;
  public string S1
  {
    get
    {
      return s1;
    }
    set
    {
      s1 = value;
      if (x1 % 2 == 0) s1 = "Numero pari";
      else s1 = "Numero dispari";
    }
  }
}

class Test
{
  public static void Main()
  {
    TC t1 = new TC();
    t1.x1 = 8;
    t1.S1 = "";
    Console.WriteLine(t1.S1);
    TC t2 = new TC();
    t2.x1 = 7;
    t2.S1 = "";
    Console.WriteLine(t2.S1);
  }
}

Alle righe 16 e 17 abbiamo la semplice valutazione di un valore. Eventualmente sarà possibile fare alcune distinzioni anche sul valore di valure con espressioni del tipo

if (value == n) { ... }
else { ... }


Analogamente si può intervenire sul valore esposto da get, tanto per dire si poteva anche scrivere:

return s1 + " ciao";

alla riga 11 dell'esempio 22.2, ma ovviamente sono possibili eleborazioni più complesse.

E' importante notare che set e get non vanno per forza in coppia, è possibile implementarne solo uno dei due. Questo evidentemente fornisce alcune caratteristiche di protezione:

-- una classe in cui sia presente solo l'sitruzione set potrà solo ricevere valori quindi è write-only
-- una classe in cui sia presente solo l'istruzione set potrà solo esporre il valore quindi è read-only.

Come si vede, un modo semplice per rendere sicure, in un senso o nell'altro, la classe. Classi read-only sono abbastanza comuni, quelle write-only piuttosto rare, per quanto riguarda la mia esperienza.

A partire dalla versione 6.0 di C# è possibile usare un'espressione più compatta invece del get, espressione che fa uso di un "fat arrow":

class TC
{
  private int x1 = 6;
  public int X1 => x1;
}

sta per

class TC
{
  private int x1 = 6;
  get
  {
    return x1;
  }
}

Molto interessanti sono anche le automatic properties o proprietà implementate automaticamente, introdotte con C# 3.0. In pratica abbiamo la possibilità, grazie a questo meccanismo, di lavorare con get e set senza esplicitare campi. In alcuni casi, come quando semplicemente di far passare dei valori o meglio quando non c'è una implementazione logioca complessa, questo può essere molto utile per risparmiare la digitazione di codice inutile ai fini pratici. Vediamo un esempio:

  Esempio 22.3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using System;

class TC
{
  public int X1 { get; set;}
}

class Test
{
  public static void Main()
  {
    TC tc1 = new TC();
    tc1.X1 = 9;
    Console.WriteLine(tc1.X1);
    Console.WriteLine(tc1.X1);
  }
}

Come si vede la riga 5 definisce una proprietà ma nessun campo viene creato. In questi casi il compilatore crea internamente un campo dello stesso tipo di quello riferito dalla proprietà ed inizializzato al valore di deafult per il tipo. Da notare che get e set, devono essere entrambe presenti, quindi non si può creare una proprietà automatica in sola lettura o sola scrittura e ovviamente ve ne possono essere più di una.