Tecniche C# avanzate (confronto tra C# e Java)
Aggiornamento: novembre 2007
C# fornisce alcuni elementi di linguaggio utili, quali indicizzatori, attributi e delegati, che consentono di utilizzare tecniche di programmazione avanzate.
Indicizzatori
Gli indicizzatori consentono di accedere a una classe o a una struttura esattamente come si accede a una matrice. Si supponga di avere una classe che rappresenta un singolo reparto di un'azienda, che la classe contenga i nomi di tutti i dipendenti che lavorano nel reparto e che gli indicizzatori consentano l'accesso a tali nomi nel seguente modo:
sales[0] = "Nikki";
sales[1] = "Becky";
Gli indicizzatori vengono attivati definendo una proprietà con la seguente firma, ad esempio, nella definizione della classe:
public string this [int index] //indexer
Sarà quindi possibile fornire metodi get e set come per una normale proprietà. Sono proprio queste funzioni di accesso a specificare a quale membro interno viene fatto riferimento quando viene utilizzato l'indicizzatore.
Nel semplice esempio riportato di seguito viene creata una classe denominata Department che utilizza gli indicizzatori per accedere ai dipendenti del reparto, rappresentati internamente come una matrice di stringhe:
public class Department
{
private string name;
private const int MAX_EMPLOYEES = 10;
private string[] employees = new string[MAX_EMPLOYEES]; //employee array
public Department(string departmentName) //constructor
{
name = departmentName;
}
public string this [int index] //indexer
{
get
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
return employees[index];
}
else
{
throw new System.IndexOutOfRangeException();
}
}
set
{
if (index >= 0 && index < MAX_EMPLOYEES)
{
employees[index] = value;
}
else
{
throw new System.IndexOutOfRangeException();
}
}
}
// code for the rest of the class...
}
È quindi possibile creare un'istanza della classe e accedervi come illustrato nell'esempio di codice riportato di seguito:
class TestDepartment
{
static void Main()
{
Department sales = new Department("Sales");
sales[0] = "Nikki";
sales[1] = "Becky";
System.Console.WriteLine("The sales team is {0} and {1}", sales[0], sales[1]);
}
}
L'output è il seguente:
The sales team is Nikki and Becky
Per ulteriori informazioni, vedere la classe Indicizzatori (Guida per programmatori C#).
Attributi
C# fornisce un meccanismo denominato attributo per l'aggiunta di informazioni dichiarative sui tipi. Gli attributi sono simili al concetto delle annotazioni in Java. Le informazioni aggiuntive relative a un tipo vengono inserite in tag dichiarativi che precedono la definizione del tipo. Negli esempi riportati di seguito viene illustrato come utilizzare gli attributi .NET Framework per decorare una classe o un metodo.
Nell'esempio riportato di seguito il metodo GetTime viene contrassegnato come servizio Web XML mediante l'aggiunta dell'attributo WebMethodAttribute.
public class Utilities : System.Web.Services.WebService
{
[System.Web.Services.WebMethod] // Attribute
public string GetTime()
{
return System.DateTime.Now.ToShortTimeString();
}
}
Con l'aggiunta dell'attributo WebMethod, si indica a .NET Framework di gestire automaticamente l'interscambio XML/SOAP necessario per chiamare questa funzione. Chiamando questo servizio Web, viene recuperato il seguente valore:
<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="http://tempuri.org/">7:26 PM</string>
Nell'esempio riportato di seguito la classe Employee viene contrassegnata come serializzabile mediante l'aggiunta dell'attributo SerializableAttribute. Benché il campo Salary sia contrassegnato come public, esso non verrà serializzato poiché è contrassegnato dall'attributo NonSerializedAttribute.
[System.Serializable()]
public class Employee
{
public int ID;
public string Name;
[System.NonSerialized()] public int Salary;
}
Per ulteriori informazioni, vedere la classe Creazione di attributi personalizzati (Guida per programmatori C#).
Delegati
Alcuni linguaggi come C++ o Pascal supportano il concetto di puntatori a funzione, che consentono di scegliere quale funzione chiamare in fase di esecuzione.
Diversamente da C#, Java non fornisce alcun costrutto con la funzionalità di un puntatore a funzione. Utilizzando la classe Delegate, un'istanza di delegato incapsula un metodo che costituisce un'entità disponibile per la chiamata.
Per i metodi di istanza, il delegato è costituito da un'istanza della classe che lo contiene e da un metodo sull'istanza. Per i metodi statici, un'entità disponibile per la chiamata è costituita da una classe e da un metodo statico sulla classe. Un delegato può pertanto essere utilizzato per richiamare una funzione di qualsiasi oggetto ed è orientato a oggetti, indipendente dai tipi e sicuro.
Per definire e utilizzare i delegati, è necessario effettuare tre operazioni:
Dichiarazione
Creazione di un'istanza
Chiamata
Per dichiarare un delegato, utilizzare la sintassi riportata di seguito:
delegate void Del1();
Questo delegato potrà poi essere utilizzato per fare riferimento a qualsiasi funzione che restituisca void e non accetti argomenti.
Analogamente, per creare un delegato per qualsiasi funzione che accetti un parametro di stringa e restituisca un risultato long, utilizzare la sintassi riportata di seguito:
delegate long Del2(string s);
È quindi possibile assegnare il delegato a un metodo qualsiasi con la firma riportata di seguito:
Del2 d; // declare the delegate variable
d = DoWork; // set the delegate to refer to the DoWork method
Dove la firma di DoWork è:
public static long DoWork(string name)
Riassegnazione dei delegati
Gli oggetti Delegate non sono modificabili, ossia la firma a cui corrispondono non può essere modificata dopo essere stata impostata. È tuttavia possibile puntare a un altro metodo, purché abbia la stessa firma. In questo caso si riassegna d a un nuovo oggetto delegato in modo che d richiami il metodo DoMoreWork. Ciò è possibile solo se DoWork e DoMoreWork hanno entrambi la stessa firma.
Del2 d; // declare the delegate variable
d = DoWork; // set the delegate to refer to the DoWork method
d = DoMoreWork; // reassign the delegate to refer to the DoMoreWork method
Chiamata dei delegati
La procedura per richiamare un delegato è piuttosto semplice. È infatti sufficiente sostituire il nome della variabile del delegato al nome del metodo: In questo modo viene richiamato il metodo Add con i valori 11 e 22 e viene restituito un risultato long assegnato alla variabile sum.
Del operation; // declare the delegate variable
operation = Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // invoke the delegate
Nell'esempio riportato di seguito viene illustrato come creare un delegato, crearne un'istanza e richiamarlo:
public class MathClass
{
public static long Add(int i, int j) // static
{
return (i + j);
}
public static long Multiply (int i, int j) // static
{
return (i * j);
}
}
class TestMathClass
{
delegate long Del(int i, int j); // declare the delegate type
static void Main()
{
Del operation; // declare the delegate variable
operation = MathClass.Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // use the delegate to call the Add method
operation = MathClass.Multiply; // change the delegate to refer to the Multiply method
long product = operation(30, 40); // use the delegate to call the Multiply method
System.Console.WriteLine("11 + 22 = " + sum);
System.Console.WriteLine("30 * 40 = " + product);
}
}
Output
11 + 22 = 33
30 * 40 = 1200
Un'istanza di un delegato deve contenere un riferimento a un oggetto. Nell'esempio sopra riportato il problema viene aggirato dichiarando i metodi come statici, nel qual caso non è necessario specificare un riferimento a un oggetto. Se tuttavia un delegato fa riferimento a un metodo di istanza, il riferimento a un oggetto deve essere specificato come illustrato di seguito:
Del operation; // declare the delegate variable
MathClass m1 = new MathClass(); // declare the MathClass instance
operation = m1.Add; // set the delegate to refer to the Add method
In questo esempio Add e Multiply sono metodi di istanza di MathClass. Se i metodi di MathClass non vengono dichiarati come statici, è necessario richiamarli tramite il delegato utilizzando un'istanza di MathClass, come riportato di seguito:
public class MathClass
{
public long Add(int i, int j) // not static
{
return (i + j);
}
public long Multiply (int i, int j) // not static
{
return (i * j);
}
}
class TestMathClass
{
delegate long Del(int i, int j); // declare the delegate type
static void Main()
{
Del operation; // declare the delegate variable
MathClass m1 = new MathClass(); // declare the MathClass instance
operation = m1.Add; // set the delegate to refer to the Add method
long sum = operation(11, 22); // use the delegate to call the Add method
operation = m1.Multiply; // change the delegate to refer to the Multiply method
long product = operation(30, 40); // use the delegate to call the Multiply method
System.Console.WriteLine("11 + 22 = " + sum);
System.Console.WriteLine("30 * 40 = " + product);
}
}
Output
In questo esempio viene fornito lo stesso output dell'esempio precedente, in cui i metodi sono stati dichiarati come statici.
11 + 22 = 33
30 * 40 = 1200
Delegati ed eventi
I delegati vengono utilizzati diffusamente anche in .NET Framework per le attività di gestione degli eventi, ad esempio un evento Click per un pulsante in un'applicazione Windows o Web. Mentre in Java gli eventi di solito vengono gestiti implementando classi listener personalizzate, gli sviluppatori C# possono utilizzare i delegati per la gestione degli eventi. Un evento viene dichiarato come un campo con un tipo delegato, con la differenza che la parola chiave event precede la dichiarazione dell'evento. Gli eventi vengono in genere dichiarati public, ma è consentito qualsiasi modificatore di accesso. Di seguito sono riportate le dichiarazioni degli oggetti delegate e event.
// Declare the delegate type:
public delegate void CustomEventHandler(object sender, System.EventArgs e);
// Declare the event variable using the delegate type:
public event CustomEventHandler CustomEvent;
I delegati di evento sono multicast, il che significa che possono mantenere riferimenti a più metodi di gestione eventi. Un delegato comunica gli eventi della classe che genera l'evento mantenendo un elenco dei gestori eventi registrati per quell'evento. Nell'esempio riportato di seguito viene illustrato come effettuare la sottoscrizione di più funzioni a un evento. La classe EventClass contiene il delegato, l'evento e il metodo per richiamare l'evento. È opportuno notare che la chiamata di un evento può essere eseguita solo dall'interno della classe che lo ha dichiarato. La classe TestEvents può quindi eseguire la sottoscrizione all'evento utilizzando l'operatore += e annullare la sottoscrizione utilizzando l'operatore -=. Quando viene eseguita una chiamata al metodo InvokeEvent, l'evento viene generato e vengono attivate in modo sincrono le eventuali funzioni con sottoscrizione all'evento, come illustrato nell'esempio riportato di seguito.
public class EventClass
{
// Declare the delegate type:
public delegate void CustomEventHandler(object sender, System.EventArgs e);
// Declare the event variable using the delegate type:
public event CustomEventHandler CustomEvent;
public void InvokeEvent()
{
// Invoke the event from within the class that declared the event:
CustomEvent(this, System.EventArgs.Empty);
}
}
class TestEvents
{
private static void CodeToRun(object sender, System.EventArgs e)
{
System.Console.WriteLine("CodeToRun is executing");
}
private static void MoreCodeToRun(object sender, System.EventArgs e)
{
System.Console.WriteLine("MoreCodeToRun is executing");
}
static void Main()
{
EventClass ec = new EventClass();
ec.CustomEvent += new EventClass.CustomEventHandler(CodeToRun);
ec.CustomEvent += new EventClass.CustomEventHandler(MoreCodeToRun);
System.Console.WriteLine("First Invocation:");
ec.InvokeEvent();
ec.CustomEvent -= new EventClass.CustomEventHandler(MoreCodeToRun);
System.Console.WriteLine("\nSecond Invocation:");
ec.InvokeEvent();
}
}
Output
First Invocation:
CodeToRun is executing
MoreCodeToRun is executing
Second Invocation:
CodeToRun is executing
Vedere anche
Attività
Concetti
Riferimenti
Delegati (Guida per programmatori C#)
Eventi (Guida per programmatori C#)