Använda indexerare (C#-programmeringsguide)
Indexerare är en syntaktisk bekvämlighet som gör att du kan skapa en klass, struct eller ett gränssnitt som klientprogram kan komma åt som en matris. Kompilatorn genererar en Item
egenskap (eller en alternativt namngiven egenskap om IndexerNameAttribute den finns) och lämpliga åtkomstmetoder. Indexerare implementeras oftast i typer vars primära syfte är att kapsla in en intern samling eller matris. Anta till exempel att du har en klass TempRecord
som representerar temperaturen i Fahrenheit enligt 10 olika tider under en 24-timmarsperiod. Klassen innehåller en temps
matris av typen float[]
som lagrar temperaturvärdena. Genom att implementera en indexerare i den här klassen kan klienterna komma åt temperaturerna i en TempRecord
instans som float temp = tempRecord[4]
i stället för som float temp = tempRecord.temps[4]
. Indexerarens notation förenklar inte bara syntaxen för klientprogram. Det gör även klassen och dess syfte mer intuitivt för andra utvecklare att förstå.
Om du vill deklarera en indexerare för en klass eller struct använder du det här nyckelordet, som följande exempel visar:
// Indexer declaration
public int this[int index]
{
// get and set accessors
}
Viktigt!
Om du deklarerar en indexerare genereras automatiskt en egenskap med namnet Item
på objektet. Egenskapen Item
är inte direkt tillgänglig från instansens medlemsåtkomstuttryck. Om du lägger till din egen Item
egenskap i ett objekt med en indexerare får du dessutom ett CS0102-kompilatorfel. Undvik det här felet genom att IndexerNameAttribute byta namn på indexeraren enligt beskrivningen senare i den här artikeln.
Kommentarer
Typen av indexerare och typen av dess parametrar måste vara minst lika tillgänglig som indexeraren själv. Mer information om hjälpmedelsnivåer finns i Åtkomstmodifierare.
Mer information om hur du använder indexerare med ett gränssnitt finns i Gränssnittsindexerare.
En indexerares signatur består av antalet och typerna av dess formella parametrar. Den innehåller inte indexerarens typ eller namnen på de formella parametrarna. Om du deklarerar fler än en indexerare i samma klass måste de ha olika signaturer.
En indexerare klassificeras inte som en variabel. Därför kan ett indexerarvärde inte skickas med referens (som en ref
eller out
parameter) om inte dess värde är en referens (dvs. det returnerar med referens.)
Om du vill ge indexeraren ett namn som andra språk kan använda använder du System.Runtime.CompilerServices.IndexerNameAttribute, som följande exempel visar:
// Indexer declaration
[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this[int index]
{
// get and set accessors
}
Indexeraren har namnet TheItem
, eftersom det åsidosättas av indexerarens namnattribut. Indexerarens namn är Item
som standard .
Exempel 1
I följande exempel visas hur du deklarerar ett privat matrisfält, temps
, och en indexerare. Indexeraren ger direkt åtkomst till instansen tempRecord[i]
. Alternativet till att använda indexeraren är att deklarera matrisen som en offentlig medlem och komma åt dess medlemmar, tempRecord.temps[i]
, direkt.
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;
}
}
Observera att när en indexerares åtkomst utvärderas, till exempel i en Console.Write
instruktion, anropas get-åtkomstorn . Om det inte finns någon get
accessor uppstår därför ett kompileringsfel.
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]}");
}
Indexering med andra värden
C# begränsar inte indexerarens parametertyp till heltal. Det kan till exempel vara användbart att använda en sträng med en indexerare. En sådan indexerare kan implementeras genom att söka efter strängen i samlingen och returnera lämpligt värde. Eftersom accessorer kan överbelastas kan sträng- och heltalsversionerna samexistera.
Exempel 2
I följande exempel deklareras en klass som lagrar veckodagarna. En get
accessor tar en sträng, namnet på en dag, och returnerar motsvarande heltal. Till exempel returnerar "Söndag" 0, "Måndag" returnerar 1 och så vidare.
// 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");
}
}
Användningsexempel 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}");
}
Exempel 3
I följande exempel deklareras en klass som lagrar veckodagarna med hjälp System.DayOfWeek av uppräkningen. En get
accessor tar ett DayOfWeek
, värdet för en dag och returnerar motsvarande heltal. Returnerar till exempel DayOfWeek.Sunday
0, DayOfWeek.Monday
returnerar 1 och så vidare.
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.");
}
}
Använda exempel 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}");
}
Robust programmering
Det finns två huvudsakliga sätt på vilka indexerares säkerhet och tillförlitlighet kan förbättras:
Se till att inkludera någon typ av strategi för felhantering för att hantera risken för att klientkoden skickar in ett ogiltigt indexvärde. I det första exemplet tidigare i den här artikeln tillhandahåller klassen TempRecord en längdegenskap som gör det möjligt för klientkoden att verifiera indata innan den skickas till indexeraren. Du kan också placera felhanteringskoden i själva indexeraren. Se till att dokumentera för användarna eventuella undantag som du genererar i en indexerare.
Ställ in tillgängligheten för get - och set-åtkomsterna så att de är så restriktiva som det är rimligt. Detta är särskilt viktigt för
set
åtkomstgivaren. Mer information finns i Begränsa hjälpmedel för accessorer.