Utilizzo degli indicizzatori (Guida per programmatori C#)

Gli indicizzatori sono una convenzione sintattica che consente di creare una classe, una struttura o un'interfaccia accessibile dalle applicazioni client, come una matrice. Gli indicizzatori sono in genere implementati in tipi il cui scopo principale consiste nell'incapsulare un insieme o una matrice interna. Ad esempio, si supponga di disporre di una classe denominata TempRecord che rappresenta la temperatura in Farenheit registrata 10 volte nell'arco di 24 ore. La classe contiene una matrice denominata "temps" di tipo float per rappresentare le temperature e un oggetto DateTime che rappresenta la data in cui sono state registrate le temperature. L'implementazione di un indicizzatore in questa classe consente ai client di accedere alle temperature in un'istanza di TempRecord come float temp = tr[4] anziché come float temp = tr.temps[4]. La notazione dell'indicizzatore non solo semplifica la sintassi per le applicazioni client, ma rende anche la classe e il relativo scopo più facilmente comprensibili ad altri sviluppatori.

Per dichiarare un indicizzatore su una classe o una struttura, utilizzare la parola chiave this, come illustrato nell'esempio seguente:

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

Note

Il tipo di un indicizzatore e il tipo dei relativi parametri devono avere almeno lo stesso livello di accessibilità dell'indicizzatore. Per ulteriori informazioni sui livelli di accessibilità, vedere Modificatori di accesso.

Per ulteriori informazioni sull'utilizzo degli indicizzatori con un'interfaccia, vedere Indicizzatori di interfaccia.

La firma di un indicizzatore è costituita dal numero e dai tipi dei parametri formali. Non include il tipo dell'indicizzatore né i nomi dei parametri formali. Se vengono dichiarati più indicizzatori nella stessa classe, questi devono avere firme diverse.

Il valore di un indicizzatore non è classificato come variabile, pertanto non è possibile passarlo come parametro ref o out.

Per fornire all'indicizzatore un nome utilizzabile in altri linguaggi, è necessario specificare un attributo name nella dichiarazione. Di seguito è riportato un esempio.

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

Questo indicizzatore avrà il nome TheItem. Se non si specifica l'attributo name, all'indicizzatore Item verrà assegnato il nome predefinito.

Esempio 1

Oggetto di descrizione

Nell'esempio che segue viene illustrata la dichiarazione di un campo di matrice privato, temps, e di un indicizzatore. L'indicizzatore consente l'accesso diretto all'istanza tempRecord[i]. In alternativa, anziché utilizzare l'indicizzatore è possibile dichiarare la matrice come membro public ed accedere direttamente ai relativi membri tempRecord.temps[i].

Si noti che, quando viene valutato l'accesso di un indicizzatore, ad esempio in un'istruzione Console.Write, viene richiamata la funzione di accesso get. Di conseguenza, se non esiste alcuna funzione di accesso get, si verificherà un errore in fase di compilazione.

Codice

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
    */

Indicizzazione mediante altri valori

In C# non è necessario utilizzare un indice di tipo integer. Ad esempio, può essere utile assegnare a un indicizzatore un valore stringa. Un indicizzatore di questo tipo può essere implementato effettuando una ricerca della stringa all'interno dell'insieme e restituendo il valore appropriato. Poiché le funzioni di accesso possono essere sottoposte a overload, le versioni basate su valore stringa e integer possono coesistere.

Esempio 2

Oggetto di descrizione

In questo esempio viene dichiarata una classe in cui sono archiviati i giorni della settimana. Viene quindi dichiarata una funzione di accesso get che accetta una stringa e il nome del giorno e restituisce l'intero corrispondente. Ad esempio, Sunday restituirà 0, Monday restituirà 1 e così via.

Codice

// 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

Programmazione robusta

Di seguito sono indicate le due tecniche principali che consentono di migliorare la sicurezza e l'affidabilità degli indicizzatori.

  • Assicurarsi di incorporare una strategia di gestione degli errori per gestire l'eventualità che nel codice client venga passato un valore di indice non valido. Nel primo esempio riportato in precedenza in questo argomento, la classe TempRecord fornisce una proprietà Length che consente al codice client di verificare l'input prima di passarlo all'indicizzatore. È anche possibile inserire il codice di gestione degli errori nell'indicizzatore stesso. Assicurarsi di documentare per gli utenti eventuali eccezioni generate all'interno di una funzione di accesso dell'indicizzatore. Per ulteriori informazioni, vedere Linee guida di progettazione delle eccezioni.

  • Impostare il livello di accessibilità delle funzioni di accesso get e set in modo abbastanza restrittivo. Questo aspetto è importante soprattutto per la funzione di accesso set. Per ulteriori informazioni, vedere Limitazione dell'accessibilità delle funzioni di accesso (Guida per programmatori C#).

Vedere anche

Riferimenti

Indicizzatori (Guida per programmatori C#)

Proprietà (Guida per programmatori C#)

Concetti

Guida per programmatori C#