Udostępnij za pomocą


18 Rozszerzone indeksowanie i fragmentowanie

18.1 Ogólne

Ta klauzula wprowadza model dla rozszerzonych typów kolekcjiz możliwością indeksowania i fragmentowania opartych na:

  • Typy wprowadzone w niniejszej klauzuli (System.Index§18.2) i System.Range (§18.3);
  • Wstępnie zdefiniowane operatory jednoargumentowe ^ (§12.9.6) i binarne .. (§12.10) oraz
  • Wyrażenie element_access .

W modelu typ jest klasyfikowany jako:

  • kolekcja, jeśli reprezentuje grupę elementóws
  • rozszerzona kolekcja indeksowalna, jeśli obsługuje wyrażenie element_access, które ma jedno wyrażenie argumentu typuIndex, które zwraca i/lub ustawia pojedynczy element typu, według wartości lub odwołania; i
  • rozszerzona kolekcja fragmentowalna, jeśli obsługuje wyrażenie element_access, które ma jedno wyrażenie argumentu typuRange, które zwraca fragment elementów typu według wartości.

Uwaga: model nie wymaga ustawienia wycinka typu, ale typ może obsługiwać go jako rozszerzenie modelu. notatka końcowa

Model jest obsługiwany w przypadku tablic jednowymiarowych (§12.8.12.2) i ciągów (§12.8.12.3).

Model może być obsługiwany przez dowolną klasę, strukturę lub typ interfejsu, który zapewnia odpowiednie indeksatory (§15.9), które implementują semantyka modelu.

Niejawna obsługa modelu jest udostępniana dla typów, które nie obsługują go bezpośrednio, ale które zapewniają określony wzorzec elementów członkowskich (§18.4). Ta obsługa jest oparta na wzorcu, a nie na podstawie semantyki, ponieważ semantyka elementów członkowskich typu, na których jest oparta, są zakładane — język nie wymusza ani nie sprawdza semantyki tych elementów członkowskich typu.

Dla celów tej klauzuli definiowane są następujące terminy:

  • Kolekcja jest typem reprezentującym grupę elementóws.
  • Kolekcja zliczalna to kolekcja, która udostępnia właściwość zliczalną właściwośćint-valued wystąpienia, której wartość jest liczbą elementów aktualnie w grupie. Ta właściwość ma nazwę Length lub Count. Pierwszy jest wybierany, jeśli oba istnieją.
  • Sekwencja lub typ indeksowalny to kolekcja:
    • które jest liczone;
    • gdzie dostęp do każdego elementu można uzyskać przy użyciu wyrażenia element_access z jednym wymaganym int argumentem, indeks od początku, dodatkowe opcjonalne argumenty są dozwolone;
    • sekwencja jest modyfikowalna , jeśli każdy element można również ustawić przy użyciu wyrażenia element_access ;
    • indeks od początku elementu to liczba elementów przed nim w sekwencji dla sekwencji zawierającej N elementów:
      • pierwsze i ostatnie elementy mają odpowiednio indeksy 0 i N-1 oraz
      • indeks przeszłości, indeks, który reprezentuje hipotetyczny element po ostatnim, ma wartość N.
  • Indeks od końca reprezentuje pozycję elementu w sekwencji względem indeksu przeszłości. W przypadku sekwencji zawierającej N elementów pierwsze, ostatnie i ostatnie indeksy są odpowiednio N, 1 i 0.
  • Zakres to ciągły przebieg zera lub większej liczby indeksów rozpoczynających się od dowolnego indeksu w ramach sekwencji.
  • Wycinek to kolekcja elementów w zakresie.
  • Kolekcja z możliwością fragmentatora jest kolekcją, która:
    • jest liczone;
    • Udostępnia metodę Slice , która przyjmuje dwa int parametry określające zakres, będąc indeksem początkowym i liczbą elementów, i zwraca nowy fragment skonstruowany z elementów w zakresie.

Powyższe definicje są rozszerzone na potrzeby zastosowań Index i Range w następujący sposób:

  • Typ jest również sekwencją , jeśli wyrażenie element_access przyjmuje jeden wymagany Index argument, a nie int argument, jest obsługiwany. W przypadku, gdy wymagane jest rozróżnienie, typ jest określany jako rozszerzony indeksowalny.
  • Typ jest również fragmentowalny , jeśli wyrażenie element_access przyjmuje jeden wymagany Range argument, a nie metodę Slice , jest obsługiwany. W przypadku gdy wymagane jest rozróżnienie, typ jest określany jako rozszerzony fragmentator.

Określa, czy typ jest klasyfikowany jako zliczany, indeksowalny lub fragmentowalny, podlega ograniczeniom ułatwień dostępu składowych (§7.5) i w związku z tym zależy od tego, gdzie jest używany typ.

Przykład: typ, w którym właściwość zliczalna i/lub indeksator są protected tylko sekwencją elementów członkowskich i wszystkich typów pochodnych. przykład końcowy

Wymagane elementy członkowskie typu do zakwalifikowania się jako sekwencja lub fragmentowalne mogą być dziedziczone.

Przykład: w poniższym kodzie

public class A
{
    public int Length { get { … } }
}

public class B : A
{
    public int this(int index) { … }
}

public class C : B
{
    public int[] Slice(int index, int count) { … }
}

Typ A jest zliczalny, B jest sekwencją i jest fragmentowalny i C sekwencja.

przykład końcowy

Note:

  • Typ może być fragmentowalny bez indeksowania ze względu na brak indeksatora (dostępnego).
  • Typ, który ma być fragmentowalny i/lub indeksowalny, wymaga, aby typ był zliczalny.
  • Chociaż elementy sekwencji są uporządkowane według pozycji w sekwencji, same elementy nie muszą być uporządkowane według ich wartości, a nawet uporządkowane.

notatka końcowa

18.2 Typ indeksu

Typ System.Index reprezentuje indeks abstrakcyjny , który reprezentuje indeks od początku lub indeks od końca.

    public readonly struct Index : IEquatable<Index>
    {
        public int Value { get; }
        public bool IsFromEnd { get; }

        public Index(int value, bool fromEnd = false);

        public static implicit operator Index(int value);
        public int GetOffset(int length);
        public bool Equals(Index other);
    }

Index wartości są konstruowane na podstawie int, określając przesunięcie nieujemne i bool, wskazując, czy przesunięcie jest od końca (true) lub początku (false). Jeśli określone przesunięcie jest ujemne, ArgumentOutOfRangeException zostanie zgłoszony.

Przykład

Index first = new Index(0, false); // first element index
var last = new Index(1, true);     // last element index
var past = new Index(0, true);     // past-end index

Index invalid = new Index(-1);     // throws ArgumentOutOfRangeException

przykład końcowy

Istnieje niejawna konwersja, z intIndex której tworzy indeksy od początku, oraz zdefiniowany język operator ^ jednoargumentowy (§12.9.6), z intIndex którego tworzy indeksy z końca.

Przykład

Korzystając z niejawnych konwersji i operatora jednoargumentowego ^ , można napisać powyższe przykłady:

Index first = 0; // first element index
var last = ^1;   // last element index
var past = ^0;   // past-end index

przykład końcowy

Metoda GetOffset konwertuje wartość abstrakcyjną Index na konkretną int wartość indeksu dla sekwencji określonego lengthelementu . Index Jeśli wartość I, , jest od końca ta metoda zwraca tę samą wartość co length - I.Value, w przeciwnym razie zwraca tę samą wartość co I.Value.

Ta metoda nie sprawdza, czy wartość zwracana znajduje się w prawidłowym 0 zakresie włącznie length-1 .

Nuta: Nie określono sprawdzania, ponieważ oczekiwane użycie wyniku polega na indeksowaniu do sekwencji z elementami length , a operacja indeksowania ma wykonać odpowiednie kontrole. notatka końcowa

Index implementuje IEquatable<Index> i wartości można porównać pod kątem równości na podstawie wartości abstrakcyjnej; dwie Index wartości są równe, jeśli i tylko wtedy, gdy odpowiednie Value i IsFromEnd właściwości są równe. Jednak Index wartości nie są uporządkowane i nie są udostępniane żadne inne operacje porównania.

Nuta:Index wartości są nieuporządkowane, ponieważ są abstrakcyjne indeksy, ogólnie rzecz biorąc, nie można określić, czy indeks od końca występuje przed lub po indeksie od początku bez odwołania do długości sekwencji. Po przekonwertowaniu na konkretne indeksy, np. według GetOffset, te konkretne indeksy są porównywalne. notatka końcowa

Index wartości mogą być używane bezpośrednio w argument_list wyrażenia element_access (§12.8.12), czyli:

  • dostęp do tablicy, a obiekt docelowy jest tablicą jednowymiarową (§12.8.12.2);
  • dostęp do ciągu (§12.8.12.3)
  • dostęp indeksatora i typ docelowy ma indeksator z odpowiednimi parametrami Index typu (§12.8.12.4) lub typu, do którego Index wartości są niejawnie konwertowane; lub
  • dostęp indeksatora i typ docelowy są zgodne ze wzorcem sekwencji, dla którego określono niejawną Index obsługę (§18.4.2).

18.3 Typ zakresu

Typ System.Range reprezentuje abstrakcyjny zakres Indexwartości od indeksu do indeksu Start , ale nie uwzględnia indeksu End .

    public readonly struct Range : IEquatable<Index>
    {
        public Index Start { get; }
        public Index End { get; }

        public Range(Index start, Index end);

        public (int Offset, int Length) GetOffsetAndLength(int length);
        public bool Equals(Range other);
    }

Range wartości są tworzone na podstawie dwóch Index wartości.

Przykład

W poniższych przykładach użyto niejawnej konwersji z int do Index (§18.2) i ^ operatora (§12.9.6), aby utworzyć Index wartości dla każdego Rangeelementu :

var firstQuad = new Range(0, 4);  // the indices from `0` to `3`
                                  // int values impicitly convert to `Index`
var nextQuad = new Range(4, 8);   // the indices from `4` to `7`
var wholeSeq = new Range(0, ^0);  // the indices from `0` to `N-1` where `N` is the
                                  // length of the sequence wholeSeq is used with
var dropFirst = new Range(1, ^0); // the indices from `1` to `N-1`
var dropLast = new Range(0, ^1);  // the indices from `0` to `N-2`
var maybeLast = new Range(^1, 6); // the indices from `N-1` to 5
var lastTwo = new Range(^2, ^0);  // the indices from `N-2` to `N-1`

przykład końcowy

Operator .. zdefiniowany przez język (§12.10) tworzy Range wartość na podstawie Index wartości.

Przykład

.. Za pomocą operatora można napisać powyższe przykłady:

var firstQuad = 0..4;  // the indices from `0` to `3`
var nextQuad = 4..8;   // the indices from `4` to `7`
var wholeSeq = 0..^0;  // the indices from `0` to `N-1`
var dropFirst = 1..^0; // the indices from `1` to `N-1`
var dropLast = 0..^1;  // the indices from `0` to `N-2`
var maybeLast = ^1..6; // the indices from `N-1` to 5
var lastTwo = ^2..^0;  // the indices from `N-2` to `N-1`

przykład końcowy

Operandy elementu .. są opcjonalne, a pierwsze wartości domyślne to 0, a druga wartość domyślna to ^0.

Przykład

Pięć z powyższych przykładów można skrócić, opierając się na wartościach domyślnych dla operandów:

var firstQuad = ..4; // the indices from `0` to `3`
var wholeSeq = ..;   // the indices from `0` to `N-1`
var dropFirst = 1..; // the indices from `1` to `N-1`
var dropLast = ..^1; // the indices from `0` to `N-2`
var lastTwo = ^2..;  // the indices from `N-2` to `N-1`

przykład końcowy

Range Wartość jest prawidłowa w odniesieniu do długości L, jeśli:

  • indeksy betonowe w odniesieniu do LRange właściwości Start i End znajdują się w zakresie od 0 do L;
  • indeks betonowy dla Start elementu nie jest większy niż indeks betonowy dla End

Metoda GetOffsetAndLength z argumentem length konwertuje wartość abstrakcyjną Range na konkretną Range wartość reprezentowaną przez krotkę. Jeśli element Range jest nieprawidłowy w odniesieniu do length metody zgłasza wartość ArgumentOutOfRangeException.

Zwrócona Range krotka jest parą formy (S, N) , w której:

  • S jest początkowym przesunięciem zakresu, czyli indeksem dla Start właściwości Range; i
  • N jest liczbą elementów w zakresie, czyli różnicą między konkretnymi indeksami właściwości End i Start ;
  • obie wartości są obliczane w odniesieniu do lengthwartości .

Wartość konkretnego zakresu jest pusta , jeśli N jest równa zero. Pusty zakres betonowy może mieć wartość równą S konkretnemu indeksowi przeszłości (§18.1), może nie być pustym zakresem. Gdy element Range używany do fragmentowania (§18.1) kolekcja jest prawidłowa i pusta w odniesieniu do tej kolekcji, wówczas wynikowy wycinek jest pustą kolekcją.

Nuta: Konsekwencją powyższego jest to, że Range wartość, która jest prawidłowa i pusta w odniesieniu do length zera, może służyć do wycinka pustej kolekcji i powoduje pusty wycinek. Różni się to od indeksowania, które zgłasza wyjątek, jeśli kolekcja jest pusta. notatka końcowa*

Przykład

Za pomocą powyższych zmiennych z elementem GetOffSetAndLength(6):

var (ix0, len0) = firstQuad.GetOffsetAndLength(6); // ix0 = 0, len0 = 4
var (ix1, len1) = nextQuad.GetOffsetAndLength(6);  // throws
   // ArgumentOutOfRangeException as range crosses sequence end
var (ix2, len2) = wholeSeq.GetOffsetAndLength(6);  // ix2 = 0, len2 = 6
var (ix3, len3) = dropFirst.GetOffsetAndLength(6); // ix3 = 1, len3 = 5
var (ix4, len4) = dropLast.GetOffsetAndLength(6);  // ix4 = 0, len4 = 5
var (ix5, len5) = maybeLast.GetOffsetAndLength(6); // ix5 = 5, len5 = 1
var (ix6, len6) = lastTwo.GetOffsetAndLength(6);   // ix6 = 4, len6 = 2

Range implementuje IEquatable<Range> i wartości można porównać pod kątem równości na podstawie wartości abstrakcyjnej; dwie Range wartości są równe, jeśli i tylko wtedy, gdy wartości abstrakcyjne odpowiednich Start właściwości i End są równe (§18.2). Jednak Range wartości nie są uporządkowane i nie są udostępniane żadne inne operacje porównania.

Nuta:Range wartości są nieurządkowane, ponieważ są abstrakcyjne i nie ma unikatowej relacji porządkowania. Po przekonwertowaniu na konkretny początek i długość, np. przez GetOffsetAndLengthparametr , można zdefiniować relację porządkowania. notatka końcowa

Range wartości mogą być używane bezpośrednio w argument_list wyrażenia element_access (§12.8.12), czyli:

  • dostęp do tablicy, a obiekt docelowy jest tablicą jednowymiarową (§12.8.12.2);
  • dostęp do ciągu (§12.8.12.3);
  • dostęp indeksatora i typ docelowy ma indeksator z odpowiednimi parametrami Range typu (§12.8.12.4) lub typu, do którego Range wartości są niejawnie konwertowane; lub
  • dostęp indeksatora (§12.8.12.4) i typ docelowy jest zgodny ze wzorcem sekwencji, dla którego określono niejawną Range obsługę (§18.4.3).

18.4 Niejawna obsługa wzorca dla indeksów i zakresów

18.4.1 Ogólne

Jeśli wyrażenie element_access (§12.8.12) formularza E[A]; gdzie E ma typ T i A jest pojedynczym wyrażeniem niejawnie konwertowanym na Index lub Range; nie można zidentyfikować jako:

  • dostęp do tablicy (§12.8.12.2),
  • dostęp do ciągu (§12.8.12.3) lub
  • dostęp indeksatora (§12.8.12.4), ponieważ T nie zapewnia odpowiedniego indeksatora dostępnego

następnie zapewniana jest niejawna obsługa wyrażenia, jeśli T jest zgodna z określonym wzorcem. Jeśli T ten wzorzec nie jest zgodny, wystąpi błąd czasu kompilacji.

Obsługa niejawnego indeksu w wersji 18.4.2

Jeśli w jakimkolwiek kontekście wyrażenie element_access (§12.8.12) formularza E[A]; gdzie E ma typ T i A jest jedno wyrażenie niejawnie konwertowane na Index; jest nieprawidłowe (§18.4.1), jeśli w tym samym kontekście:

  • T zapewnia dostępnym członkom kwalifikującym go jako sekwencję (§18.1); i
  • wyrażenie E[0] jest prawidłowe i używa tego samego indeksatora, który kwalifikuje się T jako sekwencja

następnie wyrażenie E[A] jest niejawnie wspierane.

Bez ograniczania w inny sposób implementacji niniejszego standardu kolejność obliczania wyrażenia jest równoważna:

  1. E jest obliczany;
  2. A jest obliczany;
  3. właściwość zliczana T jest obliczana, jeśli jest to wymagane przez implementację;
  4. metodę pobierania lub ustawiania metody int indeksatora opartego T na tym, który będzie używany E[0] w tym samym kontekście, jest wywoływany.

Obsługa niejawnego zakresu 18.4.3

Jeśli w jakimkolwiek kontekście wyrażenie element_access (§12.8.12) formularza E[A]; gdzie E ma typ T i A jest jedno wyrażenie niejawnie konwertowane na Range; jest nieprawidłowe (§18.4.1), jeśli w tym samym kontekście:

  • T udostępnia dostępne elementy członkowskie kwalifikujące go jako liczalne i fragmentowalne (§18.1)

następnie wyrażenie E[A] jest niejawnie wspierane.

Bez ograniczania w inny sposób implementacji niniejszego standardu kolejność obliczania wyrażenia jest równoważna:

  1. E jest obliczany;
  2. A jest obliczany;
  3. właściwość zliczana T jest obliczana, jeśli jest to wymagane przez implementację;
  4. Slice metoda jest wywoływanaT.