Použití indexerů (Průvodce programováním v C#)

Indexery jsou syntaktické pohodlí, které umožňují vytvořit třídu, strukturu nebo rozhraní , ke kterým mají klientské aplikace přístup jako pole. Kompilátor vygeneruje Item vlastnost (nebo případně pojmenovanou vlastnost, pokud IndexerNameAttribute je k dispozici) a příslušné metody přístupového objektu. Indexery se nejčastěji implementují v typech, jejichž primárním účelem je zapouzdření interní kolekce nebo pole. Předpokládejme například, že máte třídu TempRecord , která představuje teplotu v Fahrenheita, jak je zaznamenáno v 10 různých časech během 24hodinového období. Třída obsahuje temps pole typu float[] pro uložení hodnot teploty. Implementací indexeru v této třídě mohou klienti přistupovat k teplotám v TempRecord instanci jako float temp = tempRecord[4] místo jako float temp = tempRecord.temps[4]. Zápis indexeru nejen zjednodušuje syntaxi klientských aplikací; to také dělá třídu a její účel intuitivnější pro ostatní vývojáře pochopit.

Chcete-li deklarovat indexer třídy nebo struktury, použijte toto klíčové slovo, jak ukazuje následující příklad:

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

Důležité

Deklarace indexeru automaticky vygeneruje vlastnost pojmenovanou Item pro objekt. Vlastnost Item není přímo přístupná z výrazu přístupu člena instance. Pokud navíc přidáte vlastní Item vlastnost do objektu s indexerem, zobrazí se chyba kompilátoru CS0102. Chcete-li se této chybě vyhnout, použijte přejmenování indexeru IndexerNameAttribute , jak je podrobně popsáno níže.

Poznámky

Typ indexeru a typ jeho parametrů musí být alespoň tak přístupný jako samotný indexer. Další informace o úrovních přístupnosti najdete v tématu Modifikátory přístupu.

Další informace o použití indexerů s rozhraním naleznete v tématu Indexery rozhraní.

Podpis indexeru se skládá z počtu a typů jeho formálních parametrů. Nezahrnuje typ indexeru ani názvy formálních parametrů. Pokud deklarujete více indexerů ve stejné třídě, musí mít různé podpisy.

Indexer není klasifikován jako proměnná; proto nelze předat hodnotu indexeru odkazem (jako refout parametr), pokud se nejedná o odkaz (tj. vrátí odkaz.)

Pokud chcete indexeru poskytnout název, který mohou používat jiné jazyky, použijte System.Runtime.CompilerServices.IndexerNameAttribute, jak ukazuje následující příklad:

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

Tento indexer bude mít název TheItem, protože je přepsán atributem názvu indexeru. Ve výchozím nastavení je Itemnázev indexeru .

Příklad 1

Následující příklad ukazuje, jak deklarovat privátní pole pole tempsa indexer. Indexer umožňuje přímý přístup k instanci tempRecord[i]. Alternativou k použití indexeru je deklarovat pole jako veřejný člen a přistupovat k jeho členům tempRecord.temps[i], přímo.

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

Všimněte si, že když se například v Console.Write příkazu vyhodnotí přístup indexeru , vyvolá se přístup get . Pokud tedy neexistuje žádný get přístupový objekt, dojde k chybě v době kompilace.

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

Indexování pomocí jiných hodnot

Jazyk C# neomezuje typ parametru indexeru na celé číslo. Může být například užitečné použít řetězec s indexerem. Takový indexer může být implementován vyhledáním řetězce v kolekci a vrácením příslušné hodnoty. Vzhledem k tomu, že lze přetížit přístupové objekty, můžou řetězce a celé číslo existovat společně.

Příklad 2

Následující příklad deklaruje třídu, která ukládá dny v týdnu. Přistupovací get objekt přebírá řetězec, název dne a vrátí odpovídající celé číslo. Například "Sunday" vrátí hodnotu 0, "Monday" vrátí hodnotu 1 atd.

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

Využívání příkladu 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}");
}

Příklad 3

Následující příklad deklaruje třídu, která ukládá dny v týdnu pomocí výčtu System.DayOfWeek . Příslušenství get přebírá DayOfWeekhodnotu dne a vrátí odpovídající celé číslo. Například DayOfWeek.Sunday vrátí hodnotu 0, DayOfWeek.Monday vrátí hodnotu 1 atd.

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

Využívání příkladu 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}");
}

Robustní programování

Existují dva hlavní způsoby, jak zlepšit zabezpečení a spolehlivost indexerů:

  • Nezapomeňte začlenit určitý typ strategie zpracování chyb pro zpracování pravděpodobnosti předání kódu klienta neplatnou hodnotou indexu. V prvním příkladu dříve v tomto tématu, TempRecord třída poskytuje Length vlastnost, která umožňuje klientskému kódu ověřit vstup před jeho předáním indexeru. Kód zpracování chyb můžete také vložit do samotného indexeru. Nezapomeňte dokumentovat pro uživatele všechny výjimky, které vyvoláte uvnitř přístupového objektu indexeru.

  • Nastavte přístupnost přístupových objektů get a set tak omezující, jak je rozumné. To je důležité zejména pro set příslušenství. Další informace naleznete v tématu Omezení přístupnosti přístupového objektu.

Viz také