Utilisation d'indexeurs (Guide de programmation C#)

Les indexeurs vous permettent de créer facilement une classe, un struct ou une interface auxquels les applications clientes peuvent accéder, tout comme un tableau. Le plus souvent, les indexeurs sont implémentés dans les types dont l'objectif premier est d'encapsuler une collection ou un tableau interne. Par exemple, supposons que votre classe TempRecord représente la température en Farenheit enregistrée à 10 moments différents au cours d'une période de 24 heures. La classe contient un tableau de type float nommé « temps » pour représenter les températures et un DateTime qui représente la date à laquelle les températures ont été enregistrées. En implémentant un indexeur dans cette classe, les clients peuvent accéder aux températures dans une instance TempRecord sous la forme float temp = tr[4] au lieu de float temp = tr.temps[4]. La notation d'indexeur simplifie non seulement la syntaxe pour les applications clientes, mais elle permet également aux autres développeurs de mieux comprendre l'objectif de la classe.

Pour déclarer un indexeur sur une classe ou un struct, utilisez le mot clé this, comme dans l'exemple suivant :

public int this[int index]    // Indexer declaration
{
    // get and set accessors
}

Notes

Le type d'un indexeur et le type de ses paramètres doivent être au moins aussi accessibles que l'indexeur lui-même. Pour plus d'informations sur les niveaux d'accessibilité, consultez Modificateurs d'accès.

Pour plus d'informations sur l'utilisation d'indexeurs avec une interface, consultez Indexeurs d'interface.

La signature d'un indexeur est composée du nombre et des types de ses paramètres formels. Elle n'inclut pas le type de l'indexeur ni les noms des paramètres formels. Si vous déclarez plusieurs indexeurs dans la même classe, ils doivent avoir des signatures différentes.

Une valeur d'indexeur n'est pas classée en tant que variable ; vous ne pouvez donc pas la passer en tant que paramètre ref ou out.

Pour affecter à l'indexeur un nom exploitable dans d'autres langages, utilisez un attribut name dans la déclaration. Par exemple :

[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this [int index]   // Indexer declaration
{
}

Cet indexeur portera le nom TheItem. Si vous ne précisez pas le nom de l'attribut, Item est pris comme nom par défaut.

Exemple 1

Description

L'exemple suivant montre comment déclarer un champ de tableau privé, temps, et un indexeur. L'indexeur permet d'accéder directement à l'instance tempRecord[i]. Comme alternative à l'utilisation de l'indexeur, vous pouvez déclarer le tableau en tant que membre publicet accéder directement à ses membres, tempRecord.temps[i].

Notez que lorsque l'accès à un indexeur est évalué, par exemple, dans une instruction Console.Write, l'accesseur get est appelé. C'est pourquoi une erreur de compilation se produit si aucun accesseur get n'existe.

Code

class TempRecord
{
    // Array of temperature values
    private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F, 
                                            61.3F, 65.9F, 62.1F, 59.2F, 57.5F };

    // To enable client code to validate input 
    // when accessing your indexer.
    public int Length
    {
        get { return temps.Length; }
    }
    // Indexer declaration.
    // If index is out of range, the temps array will throw the exception.
    public float this[int index]
    {
        get
        {
            return temps[index];
        }

        set
        {
            temps[index] = value;
        }
    }
}

class MainClass
{
    static void Main()
    {
        TempRecord tempRecord = new TempRecord();
        // Use the indexer's set accessor
        tempRecord[3] = 58.3F;
        tempRecord[5] = 60.1F;

        // Use the indexer's get accessor
        for (int i = 0; i < 10; i++)
        {
            System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
        }

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();

    }
}
/* Output:
        Element #0 = 56.2
        Element #1 = 56.7
        Element #2 = 56.5
        Element #3 = 58.3
        Element #4 = 58.8
        Element #5 = 60.1
        Element #6 = 65.9
        Element #7 = 62.1
        Element #8 = 59.2
        Element #9 = 57.5
    */

Indexation de l'utilisation d'autres valeurs

C# ne limite pas les index au type entier. Par exemple, il peut être utile d'utiliser une chaîne avec un indexeur. Il est possible de l'implémenter en recherchant la chaîne dans la collection. La valeur appropriée est alors retournée. Comme les accesseurs peuvent être surchargés, les versions chaîne et entier peuvent coexister.

Exemple 2

Description

Cet exemple déclare une classe stockant les jours de la semaine. L'accesseur get déclaré prend une chaîne, le nom d'un jour, et retourne l'entier correspondant. Par exemple, dimanche retourne 0, lundi retourne 1 et ainsi de suite.

Code

// Using a string as an indexer value
class DayCollection
{
    string[] days = { "Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat" };

    // This method finds the day or returns -1
    private int GetDay(string testDay)
    {

        for (int j = 0; j < days.Length; j++)
        {
            if (days[j] == testDay)
            {
                return j;
            }
        }

        throw new System.ArgumentOutOfRangeException(testDay, "testDay must be in the form \"Sun\", \"Mon\", etc");
    }

    // The get accessor returns an integer for a given string
    public int this[string day]
    {
        get
        {
            return (GetDay(day));
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        DayCollection week = new DayCollection();
        System.Console.WriteLine(week["Fri"]);

        // Raises ArgumentOutOfRangeException
        System.Console.WriteLine(week["Made-up Day"]);

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }
}
// Output: 5

Programmation fiable

La sécurité et la fiabilité des indexeurs peuvent être améliorées de deux manières principales :

  • N'oubliez pas d'incorporer une stratégie de gestion des erreurs au cas où une valeur d'index non valide serait passée au code client. Dans le premier exemple décrit plus haut, la classe TempRecord fournit une propriété Length qui permet au code client de vérifier l'entrée avant de la passer à l'indexeur. Vous pouvez également placer le code de gestion des erreurs à l'intérieur de l'indexeur lui-même. N'oubliez pas d'indiquer aux utilisateurs toutes les exceptions que vous levez dans un accesseur d'indexeur. Pour plus d'informations, consultez Instructions de conception pour les exceptions.

  • Définissez l'accessibilité des accesseurs get et set afin qu'elle soit aussi restrictive que possible. C'est particulièrement important dans le cas de l'accesseur set. Pour plus d'informations, consultez Restriction d'accessibilité de l'accesseur (Guide de programmation C#).

Voir aussi

Référence

Indexeurs (Guide de programmation C#)

Propriétés (Guide de programmation C#)

Concepts

Guide de programmation C#