Indexelők használata (C# programozási útmutató)

Az indexelők olyan szintaktikai kényelmi funkciók, amelyek lehetővé teszik egy osztály, struktúra vagy felület létrehozását, amelyet az ügyfélalkalmazások tömbként érhetnek el. A fordító létrehoz egy tulajdonságot Item (vagy egy másik néven elnevezett tulajdonságot, ha IndexerNameAttribute van) és a megfelelő kiegészítő módszereket. Az indexelőket leggyakrabban olyan típusokban implementálják, amelyek elsődleges célja egy belső gyűjtemény vagy tömb beágyazása. Tegyük fel például, hogy van egy osztálya TempRecord , amely a Fahrenheitben mért hőmérsékletet 10 különböző időpontban rögzíti egy 24 órás időszakban. Az osztály egy típustömböt tempsfloat[] tartalmaz a hőmérsékleti értékek tárolásához. Ebben az osztályban egy indexelő implementálásával az ügyfelek a TempRecord példány hőmérsékleteihez juthatnak hozzá float temp = tempRecord[4] helyett float temp = tempRecord.temps[4]. Az indexelő jelölése nem csak az ügyfélalkalmazások szintaxisát egyszerűsíti; az osztályt és célját is intuitívabbá teszi más fejlesztők számára.

Ha indexelőt szeretne deklarálni egy osztályon vagy szerkezeten, használja ezt a kulcsszót az alábbi példában látható módon:

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

Fontos

Az indexelő deklarálása automatikusan létrehoz egy, az objektumon elnevezett Item tulajdonságot. A Item tulajdonság közvetlenül nem érhető el a példánytag hozzáférési kifejezéséből. Emellett ha saját Item tulajdonságot ad hozzá egy indexelővel rendelkező objektumhoz, cs0102 fordítóhiba jelenik meg. A hiba elkerülése érdekében használja az IndexerNameAttribute indexelő átnevezését a cikk későbbi részében részletezett módon.

Megjegyzések

Az indexelő típusának és paramétereinek legalább olyan hozzáférhetőnek kell lenniük, mint maga az indexelő. További információ az akadálymentességi szintekről: Access Modifiers.

További információ az indexelők felülettel való használatáról: Interfészindexelők.

Az indexelő aláírása a formális paraméterek számából és típusaiból áll. Nem tartalmazza az indexelő típusát vagy a formális paraméterek nevét. Ha ugyanabban az osztályban egynél több indexelőt deklarál, azoknak eltérő aláírásokkal kell rendelkezniük.

Az indexelők nem minősülnek változóknak; ezért az indexelő értéke nem adható át hivatkozással (például ref vagy out paraméterként), kivéve, ha az értéke maga is hivatkozás (azaz hivatkozásként tér vissza).

Ha az indexelőnek olyan nevet szeretne adni, amelyet más nyelvek is használhatnak, használja System.Runtime.CompilerServices.IndexerNameAttributeaz alábbi példában látható módon:

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

Ennek az indexelőnek a neve TheItem, mivel az indexelő névattribútuma felülírja. Alapértelmezés szerint az indexelő neve .Item

1. példa

Az alábbi példa bemutatja, tempshogyan deklarálhat egy privát tömbmezőt és egy indexelőt. Az indexelő lehetővé teszi a példány tempRecord[i]közvetlen elérését. Az indexelőt úgy is használhatja, hogy nyilvános tagként deklarálja a tömböt, tempRecord.temps[i]és közvetlenül hozzáfér a tagjaihoz.

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;
    }
}

Figyelje meg, hogy amikor egy indexelőhöz való hozzáférést kiértékelnek, például egy Console.Write utasításban, a get metódust meghívják. Ezért, ha nincs get tartozék, fordítási időhiba lép fel.

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]}");
}

Indexelés más értékekkel

A C# nem korlátozza az indexelő paraméter típusát egész számra. Például hasznos lehet egy karaktersorozat indexelővel való használata. Az ilyen indexelők a gyűjtemény sztringjének keresésével és a megfelelő érték visszaadásával implementálhatók. Mivel a lekérdezők túlterhelhetők, a sztring- és számbeli változatok együtt létezhetnek.

2. példa

Az alábbi példa egy olyan osztályt deklarál, amely a hét napjait tárolja. A get lekérdező egy karakterláncot, egy nap nevét veszi fel, és visszaadja a megfelelő egész számot. A "Vasárnap" például 0, a "Hétfő" pedig 1 értéket ad vissza, és így tovább.

// 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");
    }
}

2. példa felhasználása

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}");
}

3. példa

Az alábbi példa egy osztályt deklarál, amely a hét napjait tárolja az System.DayOfWeek enumerálás használatával. A get kiegészítő egy DayOfWeeknap értékét veszi figyelembe, és a megfelelő egész számot adja vissza. Például DayOfWeek.Sunday 0 értéket ad vissza, DayOfWeek.Monday 1-et és így tovább.

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.");
    }
}

3. példa felhasználása

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}");
}

Robusztus programozás

Az indexelők biztonságának és megbízhatóságának két fő módja van:

  • Figyeljen arra, hogy valamilyen hibakezelési stratégiát alkalmazzon az ügyfélkód által megadott érvénytelen indexérték kezelésére. A cikk korábbi első példájában a TempRecord osztály egy Length tulajdonságot biztosít, amely lehetővé teszi, hogy az ügyfélkód ellenőrizze a bemenetet, mielőtt átadja azt az indexelőnek. A hibakezelő kódot magában az indexelőben is elhelyezheti. Mindenképpen dokumentálja a felhasználók számára az indexelő tartozékon belüli kivételeket.

  • Állítsa be a get és set hozzáférők elérhetőségét a lehető legésszerűbben korlátozott szintre. Ez különösen a set tartozék szempontjából fontos. További információ: A kiegészítő akadálymentesség korlátozása.

Lásd még