Techniques C# avancées (C# et Java)
Mise à jour : novembre 2007
C# fournit quelques fonctionnalités de langage utiles, telles que les indexeurs, attributs et délégués qui permettent l'utilisation de techniques de programmation avancées.
Indexeurs
Les indexeurs offrent un moyen d'accéder à une classe ou un struct de la même façon qu'à un tableau. Par exemple, vous pouvez avoir une classe qui représente un département particulier d'une société. La classe pourrait contenir les noms de tous les employés du département, et les indexeurs pourraient vous permettre d'accéder à ces noms comme suit :
sales[0] = "Nikki";
sales[1] = "Becky";
Les indexeurs sont activés en définissant une propriété avec la signature suivante, par exemple, dans la définition de classe :
public string this [int index] //indexer
Vous fournissez ensuite les méthodes get et set comme vous le feriez pour une propriété normale, et ce sont ces accesseurs qui spécifient à quel membre interne il est fait référence et à quel moment l'indexeur est utilisé.
Dans l'exemple simple suivant, vous créez une classe appelée Department qui utilise des indexeurs pour accéder aux employés de ce département, représenté en interne sous la forme d'un tableau de chaînes :
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...
}
Vous pouvez créer ensuite une instance de cette classe et y accéder comme illustré dans l'exemple de code suivant :
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]);
}
}
Le résultat est le suivant :
The sales team is Nikki and Becky
Pour plus d'informations, consultez Indexeurs (Guide de programmation C#).
Attributs
C# fournit un mécanisme, appelé attribut, pour ajouter des informations déclaratives sur les types. Les attributs sont quelque peu semblables au concept d'annotations en Java. Les informations supplémentaires sur un type sont placées à l'intérieur de balises déclaratives qui précèdent la définition du type. Les exemples suivants montrent comment vous pouvez utiliser des attributs .NET Framework pour décorer une classe ou méthode.
Dans l'exemple ci-dessous, la méthode GetTime est marquée comme un service Web XML en ajoutant l'attribut WebMethodAttribute.
public class Utilities : System.Web.Services.WebService
{
[System.Web.Services.WebMethod] // Attribute
public string GetTime()
{
return System.DateTime.Now.ToShortTimeString();
}
}
L'ajout de l'attribut WebMethod fait que le .NET Framework prend automatiquement en charge l'échange XML/SOAP nécessaire pour appeler cette fonction. L'appel de ce service Web récupère la valeur suivante :
<?xml version="1.0" encoding="utf-8" ?>
<string xmlns="http://tempuri.org/">7:26 PM</string>
Dans l'exemple suivant, la classe Employee est marquée comme sérialisable par ajout de l'attribut SerializableAttribute. Même si le champ Salary est marqué comme public, il ne sera pas sérialisé, car il est marqué avec l'attribut NonSerializedAttribute.
[System.Serializable()]
public class Employee
{
public int ID;
public string Name;
[System.NonSerialized()] public int Salary;
}
Pour plus d'informations, consultez Création d'attributs personnalisés (Guide de programmation C#).
Délégués
Les langages tels que C++, Pascal et d'autres prennent en charge le concept de pointeurs fonction qui vous permettent de choisir quelle fonction vous voulez appeler au moment de l'exécution.
Contrairement à C#, Java ne fournit pas de construction avec la fonctionnalité d'un pointeur fonction. Par le biais de l'utilisation de la classe Delegate, une instance de délégué encapsule une méthode qui est une entité pouvant être appelée.
Pour les méthodes d'instance, le délégué se compose d'une instance de la classe conteneur et d'une méthode sur l'instance. Pour les méthodes statiques, une entité pouvant être appelée se compose d'une classe et d'une méthode statique sur la classe. Donc, un délégué peut être utilisé pour appeler une fonction de n'importe quel objet, et les délégués sont orientés objet, de type sécurisé et sûr.
La définition et l'utilisation de délégués nécessitent trois étapes :
Déclaration
Instanciation
Appel
Vous déclarez un délégué à l'aide de la syntaxe suivante :
delegate void Del1();
Ce délégué peut ensuite être utilisé pour référencer n'importe quelle fonction qui retourne void et ne prend pas d'arguments.
De même, vous utiliseriez la syntaxe suivante pour créer un délégué pour une fonction qui prend un paramètre de chaîne et retourne une valeur Long :
delegate long Del2(string s);
Vous pourriez assigner ensuite ce délégué à n'importe quelle méthode avec cette signature, comme suit :
Del2 d; // declare the delegate variable
d = DoWork; // set the delegate to refer to the DoWork method
Où la signature de DoWork est la suivante :
public static long DoWork(string name)
Réassignation de délégués
Les objets Delegate sont immuables, ce qui signifie que la signature à laquelle ils correspondent ne peut plus être changée une fois définie. Toutefois, vous pouvez pointer vers une autre méthode tant que les deux ont la même signature. Dans cet exemple, vous réassignez d à un nouvel objet délégué, afin que d appelle ensuite la méthode DoMoreWork. Vous pouvez procéder ainsi uniquement si DoWork et DoMoreWork ont la même signature.
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
Appel de délégués
L'appel d'un délégué est assez simple. Il vous suffit de substituer le nom de la variable de délégué au nom de la méthode. Cela appelle la méthode Add avec les valeurs 11 et 22 et retourne un résultat sous la forme d'un long qui est assigné à la variable 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
L'exemple suivant illustre la création, l'instanciation et l'appel d'un délégué :
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);
}
}
Sortie
11 + 22 = 33
30 * 40 = 1200
Une instance de délégué doit contenir une référence d'objet. L'exemple précédent contourne cette règle en déclarant les méthodes comme statiques, ce qui signifie qu'il n'est pas nécessaire de spécifier une référence d'objet. Toutefois, si un délégué fait référence à une méthode d'instance, la référence d'objet doit être donnée comme suit :
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
Dans cet exemple, Add et Multiply sont des méthodes d'instance de MathClass. Si les méthodes de MathClass ne sont pas déclarées comme statique, vous les appelez par le biais du délégué à l'aide d'une instance de MathClass, comme suit :
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);
}
}
Sortie
Cet exemple produit la même sortie que l'exemple précédent dans lequel les méthodes étaient déclarées comme statiques.
11 + 22 = 33
30 * 40 = 1200
Délégués et événements
Le .NET Framework fait également une grande utilisation des délégués pour les tâches de gestion d'événements tels que les clics de boutons dans une application Windows ou Web. Tandis que la gestion des événements est généralement assurée en Java par l'implémentation de classes d'écouteur personnalisées, les développeurs C# bénéficient de délégués pour la gestion d'événements. Un événement est déclaré comme un champ avec un type délégué, mais le mot clé "event" précède la déclaration de l'événement. Les événements sont généralement déclarés comme publics, mais n'importe quel modificateur d'accessibilité est autorisé. L'exemple suivant montre la déclaration d'un delegate et d'un 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;
Les délégués d'événement sont multicast, ce qui signifie qu'ils peuvent contenir des références à plusieurs méthodes de gestion des événements. Un délégué joue le rôle de répartiteur d'événement pour la classe qui déclenche l'événement en tenant à jour une liste des gestionnaires d'événements inscrits pour l'événement. L'exemple suivant montre comment vous pouvez abonner plusieurs fonctions à un événement. La classe EventClass contient le délégué, l'événement et une méthode pour appeler l'événement. Notez que l'appel d'un événement ne peut être effectué qu'à partir de la classe qui a déclaré cet événement. La classe TestEvents peut s'abonner ensuite à l'événement à l'aide de l'opérateur += puis annuler l'abonnement à l'aide de l'opérateur -=. Lorsque la méthode InvokeEvent est appelée, elle déclenche l'événement et toutes les fonctions qui se sont abonnées à l'événement se déclencheront de façon synchrone, comme dans l'exemple suivant.
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();
}
}
Sortie
First Invocation:
CodeToRun is executing
MoreCodeToRun is executing
Second Invocation:
CodeToRun is executing
Voir aussi
Tâches
Concepts
Référence
Délégués (Guide de programmation C#)
Événements (Guide de programmation C#)