DotNetCampania
Il primo portale campano dedicato allo sviluppo software con tecnologie Microsoft

Insieme verso il primo esame per la certificazione Microsoft: Esame 70-536 – Lezione 02

100% of people found this useful
Insieme verso il primo esame per la certificazione Microsoft: Esame 70-536 – Lezione 02

I Reference Type

Come già detto nella precedente lezione un tipo reference memorizza il valore della sua variabile nell’area di memoria chiamata heap, mentre mantiene il rifermento a tale area di memoria nell’area di memoria chiamata stack.

Mentre lo stack viene pulito man mano che le variabili in esso memorizzate perdono visibilità (out-of-scope), l’heap viene gestito da un sistema chiamato garbage collector, attraverso un procedimento chiamato garbage collection che consiste nel verificare periodicamente i riferimenti esistenti ad una certa area di memoria heap e pulirla quando non esistono più riferimenti di questo tipo.

Il modo più semplice di capire il funzionamento pratico di un tipo reference è quello di confrontarlo con un value type. Consideriamo ad esempio il value type personalizzato creato l’ultima volta con un esempio di utilizzo:

 

// C#

struct MioTipo

{

    public int i;

    public bool b;

}

 

class Program

{

    static void Main(string[] args)

    {

        MioTipo primo;

        primo.i = 1;

        primo.b = true;

 

        MioTipo secondo = primo;

        secondo.i = 2;

        secondo.b = false;

 

        Console.WriteLine("a: {0}, {1}; b: {2}, {3}", primo.i, primo.b, secondo.i, secondo.b);

    }

}

 

' VB

Structure MioTipo

    Dim i As Integer

    Dim b As Boolean

End Structure

 

Sub Main()

    Dim primo As MioTipo

    primo.i = 1

    primo.b = True

 

    Dim secondo As MioTipo = primo

    secondo.i = 2

    secondo.b = False

 

    Console.WriteLine("a: {0}, {1}; b: {2}, {3}", primo.i, primo.b, secondo.i, secondo.b)

End Sub

 

Il risultato in output è il seguente:

 

Per costrruire un reference type basta sotituire il costrutto struct (Structure in Visual Basic) con il costrutto Class:

 

// C#

public class MioTipo

{

    public int i;

    public bool b;

}

 

class Program

{

    static void Main(string[] args)

    {

        MioTipo primo = new MioTipo();

        primo.i = 1;

        primo.b = true;

 

        MioTipo secondo = primo;

        secondo.i = 2;

        secondo.b = false;

 

        Console.WriteLine("a: {0}, {1}; b: {2}, {3}", primo.i, primo.b, secondo.i, secondo.b);

    }

}

 

' VB

Public Class MioTipo

    Public i As Integer

    Public b As Boolean

End Class

 

Sub Main()

    Dim primo As New MioTipo

    primo.i = 1

    primo.b = True

 

    Dim secondo As MioTipo = primo

    secondo.i = 2

    secondo.b = False

 

    Console.WriteLine("a: {0}, {1}; b: {2}, {3}", primo.i, primo.b, secondo.i, secondo.b)

End Sub

 

Il risultato in questo caso invece è il seguente:

Come mai questa differenza? Molto semplicemente perché in entrambi i casi l’assegnazione alla variabile secondo del valore di primo consiste in una copia del valore delle variabili, che nel primo caso (value type) è proprio il valore della variabile, mentre nel secondo caso (reference type) si tratta del riferimento al valore della variabile, di conseguenza dopo l’assegnazione nel secondo caso secondo punta alla stessa locazione di memoria heap di primo.

Un’altra grande differenza è sicuramente l’utilizzo dell’operatore new (New in Visual Basic) che permette di istanziare la classe MioTipo, creando così lo spazio nello heap destinato alla variabile, cosa non necessaria per i value type dato che l’allocazione avviene sullo stack.

I più comuni reference type sono i seguenti:

-          System.Object, la classe base di tutti i tipi del .net framework

-          System.String, la classe per il contenimento delle stringhe di testo

-          System.Text.StringBuilder, la classe per il contenimento di stringhe di testo dinamiche

-          System.Array, la classe base di tutti gli array del framework,

-          System.IO.Stream, la classe base per la gestione degli stream di dati

-          System.Exception, la classe base di tutte le eccezioni del farmework

Del significato della parola classe base parleremo nei prossimi articoli, adesso focalizziamo la nostra attenzione sulla differenza non necessariamente ovvia tra String e StringBuilder.

String vs StringBuilder

Come potrete immaginare sul tipo String sono definite le più comuni operazioni tra stringhe quale ad esempio la somma, perché dunque avere una classe che permetta la gestione di stringhe dinamiche?

Il tipo System.String è un tipo cosiddetto immutabile, il che significa molto semplicemente che ogni volta che la stringa viene modificata, il runtime di .NET crea una nuova stringa, abbandonando la vecchia. Molti programmatori non ci fanno caso perché la cosa avviene in maniera trasparente ma sapendolo vi renderete sicuramente conto di quanto possa essere uno spreco un simile comportamento. Al fine di evitare questo spiacevole inconveniente è stata creata la classe StringBuilder:

 // C#

StringBuilder sb = new StringBuilder();

sb.Append("DotNetCampania - ");

sb.Append("Il primo User Group su .NET ");

sb.Append("tutto campano!");

Console.WriteLine(sb);

' VB

Dim sb As New System.Text.StringBuilder

sb.Append("DotNetCampania - ")

sb.Append("Il primo User Group su .NET ")

sb.Append("tutto campano!")

Console.WriteLine(sb)

Il risultato è del tutto immaginabile:

 

Di default il costruttore (il metodo della classe che inizializza lo stato di un oggetto, ne parleremo nei prossimi articoli) di StringBuilder crea un buffer di 16 byte che ingrandisce in base alle necessità, ma è possibile specificare la dimensione iniziale e anche la dimensione massima.

Creare e ordinare Arrays

Uno dei contenitori più importanti di dati, dopo la variabile, è l’array, per capirci lo possiamo vedere come un insieme contiguo di variabili dello stesso tipo indicizzate in base alla posizione nell’insieme. La creazione in .NET è molto semplice: le parentesi tonde in Visual Basic e le parentesi quadre in C#.

// C#

int[] ar = {20, 30, 10};

' VB

Dim ar() As Integer = {20, 30, 10}

Grazie alla classe stratta Array e al suo metodo statico Sort, l’ordinamento di un array è veramente un’operazione banale:

// C#

Array.Sort(ar);

Console.WriteLine("{0}, {1}, {2}", ar[0], ar[1], ar[2]);

' VB

Array.Sort(ar)

Console.WriteLine("{0}, {1}, {2}", ar(0), ar(1), ar(2))

L’ovvio risultato:

Gli stream di dati

Nel framework .NET l’accesso al disco o la scambio dati nelle comunicazioni di rete sono uniformati dal concetto di Stream, un tipo base grazie al quale è possibile unificare l’accesso ai dati tra varie fonti. Tecnicamente stiamo parlando di una classe stratta da cui vengono create delle classi specifiche della fonte dei dati:

System.IO Type

Usato per

FileStream

Scrivere e leggere da un file

MemoryStream

Scrivere e leggere dalla memoria

StreamReader

Leggere dati da un file di testo

StreamWriter

Scrivere dati in un file di testo

Grazie a queste classi operazioni come la scrittura in un file di testo diventano veramente elementari:

// C#

StreamWriter sw = new StreamWriter("text.txt");

sw.WriteLine("DotNetCampania");

sw.Close();

StreamReader sr = new StreamReader("text.txt");

Console.WriteLine(sr.ReadToEnd());

sr.Close();

' VB

Dim sw As New StreamWriter("text.txt")

sw.WriteLine("DotNetCampania")

sw.Close()

Dim sr As New StreamReader("text.txt")

Console.WriteLine(sr.ReadToEnd)

sr.Close()

Il risultato è abbastanza intuitivo:

 

Data la loro importanza approfondiremo gli stream nei prossimi articoli.

Gestione delle eccezioni

Che succede se il nostro programma sta leggendo un file da una penna usb e la nostra sorellina, con quell’aria furtiva ma con quello sguardo angelico che ci impedisce di terminarla, ci tira fuori la penna dalla porta USB? Nella speranza che la penna non si sia rotta l’unico problema e che il programma verrebbe interrotto da un’eccezione, cioè un evento inaspettato che interrompe la normale esecuzione dell’algoritmo.

Se una tale eventualità non è stata gestita molto probabilmente il programma termina miseramente con un messaggio di errore generato dal framework, ci va bene? Direi di no! Ecco che quindi ci vengono messi a disposizione i costrutti necessari a gestire una tale eventualità: il blocco Try…Catch…Finally in Visual Basic, try…catch…finally in C#.

Niente è più illuminante di un esempio:

// C#

StreamReader sr = null;

try

{

    sr = new StreamReader("text.txt");

    Console.WriteLine(sr.ReadToEnd());

}

catch (Exception ex)

{

    Console.WriteLine("Errore: " + ex.Message);

}

finally

{

    sr.Close();

}

' VB

Dim sr As StreamReader = Nothing

Try

    sr = New StreamReader("text.txt")

    Console.WriteLine(sr.ReadToEnd())

Catch ex As Exception

    Console.WriteLine("Errore: " + ex.Message)

Finally

    sr.Close()

End Try

In pratica si divide il codice in due o tre blocchi (il finally è facoltativo): nel primo, il try, si inserisce il codice da sottoporre a gestione delle eccezioni; nel secondo, il catch, si gestisce l’eccezione, nel nostro esempio abbiamo stampato un messaggio di errore; nell’eventuale terzo, il finally, si inseriscono le operazioni da eseguire in qualsiasi caso, sia di normale flusso dell’applicazione che di eccezione. Notate la dichiarazione dello StreamReader fuori dal try: è l’unico modo di renderlo visibile anche al blocco finally.

L’esempio ci da inoltre modo di notare un oggetto molto interessante, Exception, che contiene tutte le informazioni che ci posso essere utili sull’eccezione che è stata generata. La sua struttura è molto generica e viene usata come classe base per creare eccezioni più specifiche, quali ad esempio FileNotFoundException.

La cosa interessante è che mentre il blocco try e il blocco finally sono unici, possiamo creare tutti i blocchi catch che vogliamo a patto di gestire tipi di eccezioni diverse e ricordando che solamente uno sarà utilizzato in caso di eccezione. Facciamo un esempio:

// C#

StreamReader sr = null;

try

{

    sr = new StreamReader("text.txt");

    Console.WriteLine(sr.ReadToEnd());

}

catch (System.IO.FileNotFoundException ex)

{

    Console.WriteLine("File non trovato!");

}

catch (System.UnauthorizedAccessException ex)

{

    Console.WriteLine("Non di dispone dei permessi necessari!");

}

catch (Exception ex)

{

    Console.WriteLine("Errore: " + ex.Message);

}

finally

{

    sr.Close();

}

' VB

Dim sr As StreamReader = Nothing

Try

    sr = New StreamReader("text.txt")

    Console.WriteLine(sr.ReadToEnd())

Catch ex As System.IO.FileNotFoundException

    Console.WriteLine("File non trovato!")

Catch ex As System.UnauthorizedAccessException

    Console.WriteLine("Non di dispone dei permessi necessari!")

Catch ex As Exception

    Console.WriteLine("Errore: " + ex.Message)

Finally

    sr.Close()

End Try

Il bello di questo meccanismo è che possiamo decidere cosa fare in base alla tipologia di eccezione che è stata generata. Tieniamo presente che l’ordine in cui sono messi i catch non è assolutamente casuale, anzi, essendo Exception più generica sia di FileNotFoundException che di UnauthorizedAccessException, metterla in cima ai blocchi catch avrebbe provocato la completa inutilità dei blocchi sottostanti: qualsiasi eccezione sarebbe stata sempre gestita dal blocco catch Exception. Fortuna che il nostro ambiente di sviluppo sa essere molto esplicativo:

 

Conclusioni

Se non lo avete già fatto penso sia arrivato il momento di aprire visual studio  e cominciare a smanettare un po’ altrimenti farete fatica a continuare a seguire questa serie di articoli. Vi do appuntamento al prossimo articolo in cui parleremo di OOP e del contributo di Microsoft a questo paradigma di programmazione che ha rivoluzionato il modo di programmare.

Happy Coding. 

Recent Comments

By: Liccardi Antonio Posted on 30 giu 2009 19:09
100% of people found this useful

well done!

Associazione Culturale DotNetCampania - C.F.: 95127870632

Powered by Community Server (Commercial Edition), by Telligent Systems