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

Délégués, exemple

Concepts

Guide de programmation C#

Référence

Délégués (Guide de programmation C#)

Événements (Guide de programmation C#)

Autres ressources

Langage de programmation C# pour les développeurs Java