Usando indexadores (Guia de Programação em C#)Using indexers (C# Programming Guide)

Os indexadores são uma conveniência sintática que permitem que você crie uma classe, estruturaou interface que os aplicativos cliente possam acessar como uma matriz.Indexers are a syntactic convenience that enable you to create a class, struct, or interface that client applications can access as an array. O compilador gerará uma Item Propriedade (ou uma propriedade nomeada de forma alternativa IndexerNameAttribute , se estiver presente) e os métodos acessadores apropriados.The compiler will generate an Item property (or an alternatively named property if IndexerNameAttribute is present), and the appropriate accessor methods. Os indexadores são implementados em tipos cuja principal finalidade é encapsular uma coleção ou matriz interna.Indexers are most frequently implemented in types whose primary purpose is to encapsulate an internal collection or array. Por exemplo, suponha que você tenha uma classe TempRecord que representa a temperatura em Fahrenheit, conforme registrada em 10 horas diferentes durante um período de 24 horas.For example, suppose you have a class TempRecord that represents the temperature in Fahrenheit as recorded at 10 different times during a 24-hour period. A classe contém uma temps matriz do tipo float[] para armazenar os valores de temperatura.The class contains a temps array of type float[] to store the temperature values. Ao implementar um indexador nessa classe, os clientes podem acessar as temperaturas em uma instância TempRecord como float temp = tempRecord[4], e não como float temp = tempRecord.temps[4].By implementing an indexer in this class, clients can access the temperatures in a TempRecord instance as float temp = tempRecord[4] instead of as float temp = tempRecord.temps[4]. A notação do indexador não apenas simplifica a sintaxe para aplicativos cliente; Ele também torna a classe e sua finalidade mais intuitiva para que outros desenvolvedores entendam.The indexer notation not only simplifies the syntax for client applications; it also makes the class, and its purpose more intuitive for other developers to understand.

Para declarar um indexador em uma classe ou struct, use a palavra-chave this, como mostra o seguinte exemplo:To declare an indexer on a class or struct, use the this keyword, as the following example shows:

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

Importante

A declaração de um indexador irá gerar automaticamente uma propriedade chamada Item no objeto.Declaring an indexer will automatically generate a property named Item on the object. A Item propriedade não pode ser acessada diretamente da expressão de acesso de membrode instância.The Item property is not directly accessible from the instance member access expression. Além disso, se você adicionar sua própria Item Propriedade a um objeto com um indexador, obterá um erro do compilador CS0102.Additionally, if you add your own Item property to an object with an indexer, you'll get a CS0102 compiler error. Para evitar esse erro, use IndexerNameAttribute renomear o indexador conforme detalhado abaixo.To avoid this error, use the IndexerNameAttribute rename the indexer as detailed below.

ComentáriosRemarks

O tipo de um indexador e o tipo dos seus parâmetros devem ser pelo menos tão acessíveis quanto o próprio indexador.The type of an indexer and the type of its parameters must be at least as accessible as the indexer itself. Para obter mais informações sobre níveis de acessibilidade, consulte Modificadores de acesso.For more information about accessibility levels, see Access Modifiers.

Para obter mais informações sobre como usar indexadores com uma interface, consulte Indexadores de Interface.For more information about how to use indexers with an interface, see Interface Indexers.

A assinatura de um indexador consiste do número e dos tipos de seus parâmetros formais.The signature of an indexer consists of the number and types of its formal parameters. Ela não inclui o tipo de indexador nem os nomes dos parâmetros formais.It doesn't include the indexer type or the names of the formal parameters. Se você declarar mais de um indexador na mesma classe, eles terão diferentes assinaturas.If you declare more than one indexer in the same class, they must have different signatures.

Um valor de indexador não é classificado como uma variável; portanto, não é possível passar um valor de indexador como um parâmetro ref ou out.An indexer value is not classified as a variable; therefore, you cannot pass an indexer value as a ref or out parameter.

Para fornecer o indexador com um nome que outras linguagens possam usar, use System.Runtime.CompilerServices.IndexerNameAttribute, como mostra o seguinte exemplo:To provide the indexer with a name that other languages can use, use System.Runtime.CompilerServices.IndexerNameAttribute, as the following example shows:

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

Esse indexador terá o nome TheItem , pois ele é substituído pelo atributo de nome do indexador.This indexer will have the name TheItem, as it is overridden by the indexer name attribute. Por padrão, o nome do indexador é Item .By default, the indexer name is Item.

Exemplo 1Example 1

O exemplo a seguir mostra como declarar um campo de matriz privada, temps e um indexador.The following example shows how to declare a private array field, temps, and an indexer. O indexador permite acesso direto à instância tempRecord[i].The indexer enables direct access to the instance tempRecord[i]. A alternativa ao uso do indexador é declarar a matriz como um membro público e acessar seus membros, tempRecord.temps[i], diretamente.The alternative to using the indexer is to declare the array as a public member and access its members, tempRecord.temps[i], directly.

public class TempRecord
{
    // Array of temperature values
    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 => temps.Length;
    
    // Indexer declaration.
    // If index is out of range, the temps array will throw the exception.
    public float this[int index]
    {
        get => temps[index];
        set => temps[index] = value;
    }
}

Observe que, quando o acesso de um indexador é avaliado, por exemplo, em uma instrução Console.Write, o acessador get é invocado.Notice that when an indexer's access is evaluated, for example, in a Console.Write statement, the get accessor is invoked. Portanto, se não existir nenhum acessador get, ocorrerá um erro em tempo de compilação.Therefore, if no get accessor exists, a compile-time error occurs.

using System;

class Program
{
    static void Main()
    {
        var 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++)
        {
            Console.WriteLine($"Element #{i} = {tempRecord[i]}");
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        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
    */
}

Indexando usando outros valoresIndexing using other values

O C# não limita o tipo de parâmetro do indexador ao inteiro.C# doesn't limit the indexer parameter type to integer. Por exemplo, talvez seja útil usar uma cadeia de caracteres com um indexador.For example, it may be useful to use a string with an indexer. Esse indexador pode ser implementado pesquisando a cadeia de caracteres na coleção e retornando o valor adequado.Such an indexer might be implemented by searching for the string in the collection, and returning the appropriate value. Como os acessadores podem ser sobrecarregados, as versões de cadeia de caracteres e inteiros podem coexistir.As accessors can be overloaded, the string and integer versions can coexist.

Exemplo 2Example 2

O exemplo a seguir declara uma classe que armazena os dias da semana.The following example declares a class that stores the days of the week. Um acessador get aceita uma cadeia de caracteres, o nome de um dia e retorna o inteiro correspondente.A get accessor takes a string, the name of a day, and returns the corresponding integer. Por exemplo, "Sunday" retorna 0, "Monday" retorna 1 e assim por diante.For example, "Sunday" returns 0, "Monday" returns 1, and so on.

using System;

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

    // Indexer with only a get accessor with the expression-bodied definition:
    public int this[string day] => FindDayIndex(day);

    private int FindDayIndex(string day)
    {
        for (int j = 0; j < days.Length; j++)
        {
            if (days[j] == day)
            {
                return j;
            }
        }

        throw new ArgumentOutOfRangeException(
            nameof(day),
            $"Day {day} is not supported.\nDay input must be in the form \"Sun\", \"Mon\", etc");
    }
}

Exemplo de consumo 2Consuming example 2

using System;

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

        try
        {
            Console.WriteLine(week["Made-up day"]);
        }
        catch (ArgumentOutOfRangeException e)
        {
            Console.WriteLine($"Not supported input: {e.Message}");
        }
    }
    // Output:
    // 5
    // Not supported input: Day Made-up day is not supported.
    // Day input must be in the form "Sun", "Mon", etc (Parameter 'day')
}

Exemplo 3Example 3

O exemplo a seguir declara uma classe que armazena os dias da semana usando a System.DayOfWeek enumeração.The following example declares a class that stores the days of the week using the System.DayOfWeek enum. Um get acessador pega um DayOfWeek , o valor de um dia e retorna o inteiro correspondente.A get accessor takes a DayOfWeek, the value of a day, and returns the corresponding integer. Por exemplo, DayOfWeek.Sunday retorna 0, DayOfWeek.Monday retorna 1 e assim por diante.For example, DayOfWeek.Sunday returns 0, DayOfWeek.Monday returns 1, and so on.

using System;
using Day = System.DayOfWeek;

class DayOfWeekCollection
{
    Day[] days =
    {
        Day.Sunday, Day.Monday, Day.Tuesday, Day.Wednesday,
        Day.Thursday, Day.Friday, Day.Saturday
    };

    // Indexer with only a get accessor with the expression-bodied definition:
    public int this[Day day] => FindDayIndex(day);

    private int FindDayIndex(Day day)
    {
        for (int j = 0; j < days.Length; j++)
        {
            if (days[j] == day)
            {
                return j;
            }
        }
        throw new ArgumentOutOfRangeException(
            nameof(day),
            $"Day {day} is not supported.\nDay input must be a defined System.DayOfWeek value.");
    }
}

Exemplo de consumo 3Consuming example 3

using System;

class Program
{
    static void Main()
    {
        var week = new DayOfWeekCollection();
        Console.WriteLine(week[DayOfWeek.Friday]);

        try
        {
            Console.WriteLine(week[(DayOfWeek)43]);
        }
        catch (ArgumentOutOfRangeException e)
        {
            Console.WriteLine($"Not supported input: {e.Message}");
        }
    }
    // Output:
    // 5
    // Not supported input: Day 43 is not supported.
    // Day input must be a defined System.DayOfWeek value. (Parameter 'day')
}

Programação robustaRobust programming

Há duas maneiras principais nas quais a segurança e a confiabilidade de indexadores podem ser melhoradas:There are two main ways in which the security and reliability of indexers can be improved:

  • Certifique-se de incorporar algum tipo de estratégia de tratamento de erros para manipular a chance de passagem de código cliente em um valor de índice inválido.Be sure to incorporate some type of error-handling strategy to handle the chance of client code passing in an invalid index value. Anteriormente, no primeiro exemplo neste tópico, a classe TempRecord oferece uma propriedade Length que permite que o código cliente verifique a saída antes de passá-la para o indexador.In the first example earlier in this topic, the TempRecord class provides a Length property that enables the client code to verify the input before passing it to the indexer. Também é possível colocador o código de tratamento de erro dentro do próprio indexador.You can also put the error handling code inside the indexer itself. Certifique-se documentar para os usuários as exceções que você gera dentro de um acessador do indexador.Be sure to document for users any exceptions that you throw inside an indexer accessor.

  • Defina a acessibilidade dos acessadores get e set para que ela seja mais restritiva possível.Set the accessibility of the get and set accessors to be as restrictive as is reasonable. Isso é importante para o acessador set em particular.This is important for the set accessor in particular. Para obter mais informações, consulte Restringindo a acessibilidade aos acessadores.For more information, see Restricting Accessor Accessibility.

Veja tambémSee also