Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Os indexadores são uma conveniência sintática que permite criar uma classe, um struct ou uma interface que os aplicativos clientes podem acessar como uma matriz. O compilador gera uma propriedade Item
(ou uma propriedade de nome alternativo, se IndexerNameAttribute estiver presente) e os métodos acessadores apropriados. Os indexadores são implementados em tipos cuja principal finalidade é encapsular uma coleção ou matriz interna. Por exemplo, suponha que você tenha uma classe TempRecord
que representa a temperatura em Fahrenheit, conforme registrada em 10 momentos diferentes durante um período de 24 horas. A classe contém uma matriz temps
do tipo float[]
para armazenar os valores de temperatura. 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]
. A notação do indexador não apenas simplifica a sintaxe para aplicativos clientes, mas também torna a classe e a finalidade dela mais intuitivas para que os outros desenvolvedores entendam.
Para declarar um indexador em uma classe ou struct, use a palavra-chave this, como mostra o seguinte exemplo:
// Indexer declaration
public int this[int index]
{
// get and set accessors
}
Importante
Declarar um indexador vai gerar automaticamente uma propriedade chamada Item
no objeto. A propriedade Item
não pode ser acessada diretamente por meio da expressão de acesso a membro da instância. Além disso, se você adicionar a sua propriedade Item
a um objeto com indexador, será gerado um erro do compilador CS0102. Para evitar esse erro, use a opção de IndexerNameAttribute renomear o indexador, conforme detalhado mais para frente neste artigo.
Comentários
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. Para obter mais informações sobre níveis de acessibilidade, consulte Modificadores de acesso.
Para obter mais informações sobre como usar indexadores com uma interface, consulte Indexadores de Interface.
A assinatura de um indexador consiste do número e dos tipos de seus parâmetros formais. Ela não inclui o tipo de indexador nem os nomes dos parâmetros formais. Se você declarar mais de um indexador na mesma classe, eles terão diferentes assinaturas.
Um indexador não é classificado como uma variável; portanto, um valor de indexador não pode ser passado por referência (como um parâmetro ref
ou out
), a menos que seu valor seja uma referência (ou seja, ele retorna por referência).
Para fornecer o indexador com um nome que outras linguagens possam usar, use System.Runtime.CompilerServices.IndexerNameAttribute, como mostra o seguinte exemplo:
// Indexer declaration
[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this[int index]
{
// get and set accessors
}
Esse indexador tem o nome TheItem
, pois ele é substituído pelo atributo de nome do indexador. Por padrão, o nome do indexador é Item
.
Exemplo 1
O exemplo a seguir mostra como declarar um campo de matriz privada, temps
e um indexador. O indexador permite acesso direto à instância tempRecord[i]
. A alternativa ao uso do indexador é declarar a matriz como um membro público e acessar seus membros, tempRecord.temps[i]
, diretamente.
public class TempRecord
{
// Array of temperature values
float[] temps =
[
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. Portanto, se não existir nenhum acessador get
, ocorrerá um erro em tempo de compilação.
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]}");
}
Indexando usando outros valores
O C# não limita o tipo de parâmetro do indexador ao inteiro. Por exemplo, pode ser útil usar uma cadeia de caracteres com um indexador. Esse indexador pode ser implementado pesquisando a cadeia de caracteres na coleção e retornando o valor adequado. Como os acessadores podem ser sobrecarregados, as versões do inteiro e da cadeia de caracteres podem coexistir.
Exemplo 2
O exemplo a seguir declara uma classe que armazena os dias da semana. Um acessador get
aceita uma cadeia de caracteres, o nome de um dia e retorna o inteiro correspondente. Por exemplo, "Sunday" retorna 0, "Monday" retorna 1 e assim por diante.
// 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 2
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}");
}
Exemplo 3
O exemplo a seguir declara uma classe que armazena os dias da semana usando a enumeração System.DayOfWeek. Um acessador get
aceita DayOfWeek
, o valor de um dia, e retorna o inteiro correspondente. Por exemplo, DayOfWeek.Sunday
retorna 0, DayOfWeek.Monday
retorna 1 e assim por diante.
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 3
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}");
}
Programação robusta
Há duas maneiras principais nas quais a segurança e a confiabilidade de indexadores podem ser melhoradas:
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. Anteriormente, no primeiro exemplo neste artigo, 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. Também é possível colocador o código de tratamento de erro dentro do próprio indexador. Certifique-se documentar para os usuários as exceções que você gera dentro de um acessador do indexador.
Defina a acessibilidade dos acessadores get e set para que ela seja mais restritiva possível. Isso é importante para o acessador
set
em particular. Para obter mais informações, consulte Restringindo a acessibilidade aos acessadores.