Sobes.tech
Junior

Czym są indeksy w C#? Jakie rodzaje indeksów istnieją i czy używałeś ich w praktyce?

sobes.tech AI

Odpowiedź od AI

Indeksatory w C# pozwalają na dostęp do instancji klasy lub struktury jak do tablicy, używając składni []. Nie są statyczne i przeciążają operator [].

Indeksatory

Indeksatory pozwalają na zdefiniowanie logiki dostępu do elementów kolekcji lub struktury danych w obrębie klasy.

public class MyCollection
{
    private string[] data = new string[10];

    // Indeksator umożliwiający pobieranie lub ustawianie elementu po indeksie
    public string this[int index]
    {
        get
        {
            // Sprawdzenie granic
            if (index >= 0 && index < data.Length)
            {
                return data[index];
            }
            else
            {
                throw new IndexOutOfRangeException();
            }
        }
        set
        {
            // Sprawdzenie granic
            if (index >= 0 && index < data.Length)
            {
                data[index] = value;
            }
            else
            {
                throw new IndexOutOfRangeException();
            }
        }
    }
}

Typy parametrów indeksatorów

Indeksatory mogą przyjmować dowolną liczbę parametrów dowolnego typu, nie ograniczając się tylko do typów całkowitych.

public class DictionaryLike
{
    private Dictionary<string, string> _dictionary = new Dictionary<string, string>();

    // Indeksator z parametrem typu string
    public string this[string key]
    {
        get
        {
            if (_dictionary.ContainsKey(key))
            {
                return _dictionary[key];
            }
            else
            {
                return null; // lub rzucić wyjątek
            }
        }
        set
        {
            _dictionary[key] = value;
        }
    }
}

Przeciążanie indeksatorów

Można tworzyć przeciążone indeksatory z różnymi sygnaturami (zestawem i typami parametrów).

public class Matrix
{
    private int[,] data;

    public Matrix(int rows, int cols)
    {
        data = new int[rows, cols];
    }

    // Indeksator z dwoma parametrami typu int
    public int this[int row, int col]
    {
        get
        {
            // Sprawdzenie granic
            if (row >= 0 && row < data.GetLength(0) && col >= 0 && col < data.GetLength(1))
            {
                return data[row, col];
            }
            else
            {
                throw new IndexOutOfRangeException();
            }
        }
        set
        {
            // Sprawdzenie granic
            if (row >= 0 && row < data.GetLength(0) && col >= 0 && col < data.GetLength(1))
            {
                data[row, col] = value;
            }
            else
            {
                throw new IndexOutOfRangeException();
            }
        }
    }
}

Zakresowe indeksy (Ranges & Indices)

W C# 8.0 wprowadzone zostały nowe typy System.Index i System.Range, które pozwalają na użycie składni do odwoływania się do elementów od końca kolekcji (^) oraz do uzyskiwania podzakresów (..). Te pojęcia często nazywa się "indeksami", ale różnią się od klasycznych indeksatorów.

using System;

public class ArrayExample
{
    public static void Main(string[] args)
    {
        string[] words = { "the", "quick", "brown", "fox", "jumps", "over", "the", "lazy", "dog" };

        // Użycie System.Index do dostępu od końca
        Index last = ^1; // Indeks ostatniego elementu
        Console.WriteLine(words[last]); // dog

        // Użycie System.Range do uzyskania podzakresu
        Range middle = 1..4; // Elementy od indeksu 1 do (ale nie włącznie) indeksu 4
        string[] subset = words[middle]; // {"quick", "brown", "fox"}

        // Range z użyciem System.Index
        Range fromEnd = ^3..; // od trzeciego od końca do końca
        string[] suffix = words[fromEnd]; // {"the", "lazy", "dog"}
    }
}

Praktyczne zastosowanie

Aktywnie korzystałem z indeksatorów podczas tworzenia własnych klas kolekcji lub opakowań nad istniejącymi, aby zapewnić bardziej intuicyjny dostęp do danych. Na przykład:

  • Tworzenie klas reprezentujących macierze lub dane wielowymiarowe, gdzie dostęp po współrzędnych matrix[row, col] miał sens.
  • Tworzenie opakowań nad Dictionary<> lub innymi kolekcjami, gdzie dostęp po kluczu przez indeksator poprawiał czytelność kodu.
  • W prostych kolekcjach, gdzie potrzebna była składniowa wygoda dostępu, podobna do tablicy.

Indeksy zakresowe (System.Index i System.Range) używałem do bardziej zwięzłego dostępu do części tablic lub list, co jest szczególnie przydatne przy pracy z podłańcuchami lub częściami danych z kolekcji.