Condividi tramite


18 Indicizzazione estesa e sezionamento

18.1 Generale

Questa clausola introduce un modello per i tipi di raccoltaindicizzati e sezionabiliestesi basati su:

  • I tipi introdotti in questa clausola , System.Index (§18.2) e System.Range (§18.3);
  • Operatori unari ^ predefiniti (§12.9.6) e binari .. (§12.10) e
  • Espressione element_access .

Nel modello un tipo è classificato come:

  • una raccolta se rappresenta un gruppo di elementi
  • una raccolta indicizzata estesa se supporta un'espressione element_access che dispone di una singola espressione di argomento di tipo Index che restituisce e/o imposta un singolo elemento del tipo, per valore o per riferimento; e
  • una raccolta con filtro dei dati estesa se supporta un'espressione element_access che dispone di un'unica espressione di argomento di tipo Range che restituisce una sezione degli elementi del tipo in base al valore.

Nota: il modello non richiede che sia possibile impostare una sezione del tipo, ma un tipo può supportarlo come estensione del modello. nota finale

Il modello è supportato per matrici unidimensionali (§12.8.12.2) e stringhe (§12.8.12.3).

Il modello può essere supportato da qualsiasi classe, struct o tipo di interfaccia che fornisce indicizzatori appropriati (§15.9) che implementano la semantica del modello.

Il supporto implicito per il modello viene fornito per i tipi che non lo supportano direttamente, ma che forniscono un determinato modello di membri (§18.4). Questo supporto è basato su criteri, anziché basato sulla semantica, in quanto si presuppone la semantica dei membri di tipo su cui si basa, ovvero il linguaggio non applica o controlla la semantica di questi membri di tipo.

Ai fini di questa clausola sono definiti i termini seguenti:

  • Una raccolta è un tipo che rappresenta un gruppo di elementis.
  • Una raccolta conteggiabile è una che fornisce una proprietà conteggiabile di una intproprietà dell'istanza con valori di cui il valore è il numero di elementi attualmente presenti nel gruppo. Questa proprietà deve essere denominata Length o Count. Il primo viene scelto se esistono entrambi.
  • Un tipo sequenza o indicizzabile è una raccolta:
    • che è conteggiabile;
    • in cui è possibile accedere a ogni elemento usando un'espressione element_access con un singolo argomento obbligatorio int , l'indice from-start, sono consentiti argomenti facoltativi aggiuntivi;
    • una sequenza è modificabile se ogni elemento può essere impostato anche usando un'espressione element_access ;
    • L'indice dall'inizio di un elemento è il numero di elementi prima della sequenza, per una sequenza contenente N elementi:
      • i primi e gli ultimi elementi hanno rispettivamente indici 0 e N-1 e
      • l'indice passato, un indice che rappresenta un elemento ipotetico dopo l'ultimo, ha il valore N.
  • Un indice from-end rappresenta la posizione di un elemento all'interno di una sequenza rispetto all'indice di fine precedente. Per una sequenza contenente gli N elementi il primo, l'ultimo e gli indici precedenti sono rispettivamente N, 1 e 0.
  • Un intervallo è un'esecuzione contigua di zero o più indici a partire da qualsiasi indice all'interno di una sequenza.
  • Una sezione è la raccolta di elementi all'interno di un intervallo.
  • Una raccolta sezionabile è una delle seguenti:
    • è conteggiabile;
    • fornisce un metodo Slice che accetta due int parametri che specificano un intervallo, ovvero un indice iniziale e un conteggio di elementi rispettivamente, e restituisce una nuova sezione costruita dagli elementi dell'intervallo.

Le definizioni precedenti vengono estese per gli usi di e Index come indicato di Range seguito:

  • Un tipo è anche una sequenza se è supportata un'espressione element_access che accetta un singolo argomento obbligatorio Index , anziché un int argomento. Se è necessaria una distinzione, il tipo è definito indicizzatore esteso.
  • Un tipo è anche se è supportata un'espressione element_access che accetta un singolo argomento obbligatorio Range , anziché un Slice metodo. Se è necessaria una distinzione, il tipo è definito sezionabile esteso.

Se un tipo è classificato come conteggiabile, indicizzabile o sezionabile è soggetto ai vincoli di accessibilità dei membri (§7.5) e quindi dipende dalla posizione in cui viene utilizzato il tipo.

Esempio: un tipo in cui la proprietà conteggiabile e/o l'indicizzatore sono protected solo una sequenza ai membri di se stesso e a qualsiasi tipo derivato. esempio finale

I membri necessari per un tipo da qualificare come sequenza o sezionabile possono essere ereditati.

Esempio: nel codice seguente

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) { … }
}

Il tipo A è conteggiabile, B è una sequenza ed C è sezionabile e una sequenza.

esempio finale

Note:

  • Un tipo può essere sezionabile senza essere indicizzato a causa della mancanza di un indicizzatore (accessibile).
  • Affinché un tipo possa essere sezionabile e/o indicizzabile, è necessario che il tipo sia conteggiabile.
  • Mentre gli elementi di una sequenza vengono ordinati in base alla posizione all'interno della sequenza, gli elementi stessi non devono essere ordinati in base al valore o anche ordinabili.

nota finale

18.2 Tipo di indice

Il System.Index tipo rappresenta un indice astratto che rappresenta un indice from-start o un indice from-end.

    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 i valori vengono costruiti da un intoggetto , specificando l'offset non negativo e un boologgetto , che indica se l'offset è dalla fine (true) o dall'inizio (false). Se l'offset specificato è negativo, viene generata un'eccezione ArgumentOutOfRangeException .

Esempio

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

esempio finale

Esiste una conversione implicita da int a Index cui produce indici dall'inizio e un operatore ^ unario definito dal linguaggio (§12.9.6) da cui int produce indici from-endIndex.

Esempio

È possibile scrivere le conversioni implicite e l'operatore unario ^ gli esempi precedenti:

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

esempio finale

Il metodo GetOffset converte da un valore astratto Index a un valore di indice concreto int per una sequenza dell'oggetto specificato length. Se il Index valore , Iè from-end, questo metodo restituisce lo stesso valore di length - I.Value, in caso contrario restituisce lo stesso valore di I.Value.

Questo metodo non verifica che il valore restituito sia compreso nell'intervallo valido compreso 0length-1 tra inclusi.

Nota: Non viene specificato alcun controllo perché l'uso previsto del risultato consiste nell'indicizzare in una sequenza con length elementi e l'operazione di indicizzazione deve eseguire i controlli appropriati. nota finale

Index implementa e i valori possono essere confrontati IEquatable<Index> per l'uguaglianza in base al valore astratto; due Index valori sono uguali se e solo se le rispettive Value proprietà e IsFromEnd sono uguali. Tuttavia Index , i valori non vengono ordinati e non vengono fornite altre operazioni di confronto.

Nota:Index I valori non sono ordinati perché sono indici astratti, è in generale impossibile determinare se un indice from-end precede o dopo un indice dall'inizio senza riferimento a una lunghezza di sequenza. Dopo la conversione in indici concreti, ad esempio per GetOffset, tali indici concreti sono confrontabili. nota finale

Index i valori possono essere usati direttamente nella argument_list di un'espressione element_access (§12.8.12), ovvero:

  • un accesso a una matrice e la destinazione è una matrice unidimensionale (§12.8.12.2);
  • accesso a stringhe (§12.8.12.3)
  • un accesso all'indicizzatore e il tipo di destinazione dispone di un indicizzatore con parametri corrispondenti di Index tipo (§12.8.12.4) o di un tipo a cui Index i valori sono convertibili in modo implicito; o
  • un accesso all'indicizzatore e il tipo di destinazione è conforme a un modello di sequenza per il quale è specificato il supporto implicito Index (§18.4.2).

18.3 Tipo di intervallo

Il System.Range tipo rappresenta l'intervallo astratto di Indexes da un Start indice fino a, ma non incluso, un End indice.

    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 i valori vengono costruiti da due Index valori.

Esempio

Negli esempi seguenti viene usata la conversione implicita da int a Index (§18.2) e dall'operatore ^ (§12.9.6) per creare i Index valori per ogni Range:

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`

esempio finale

L'operatore .. definito dalla lingua (§12.10) crea un Range valore da Index valori.

Esempio

È possibile scrivere l'operatore usando l'operatore .. precedente:

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`

esempio finale

Gli operandi di .. sono facoltativi, il primo valore predefinito è 0, il secondo valore predefinito è ^0.

Esempio

Cinque degli esempi precedenti possono essere abbreviati basandosi sui valori predefiniti per gli operandi:

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`

esempio finale

Un Range valore è valido rispetto a una lunghezza L se:

  • gli indici concreti in relazione all'LRange delle proprietà Start e sono compresi nell'intervallo compreso tra 0 e End; e
  • l'indice concreto per Start non è maggiore dell'indice concreto per End

Il metodo GetOffsetAndLength con un argomento length converte un valore astratto Range in un valore concreto Range rappresentato dalla tupla. Se l'oggetto Range non è valido per quanto riguarda length il metodo genera ArgumentOutOfRangeException.

La tupla concreta Range restituita è una coppia del formato (S, N) in cui:

  • S è l'offset iniziale dell'intervallo, essendo l'indice concreto per la Start proprietà di Range; e
  • N è il numero di elementi nell'intervallo, essendo la differenza tra gli indici concreti per le End proprietà e Start ;
  • entrambi i valori calcolati rispetto a length.

Un valore di intervallo concreto è vuoto se N è zero. Un intervallo concreto vuoto può avere un S valore uguale all'indice di fine passato concreto (§18.1), un intervallo non vuoto potrebbe non essere. Quando un Range oggetto utilizzato per sezionare (§18.1) una raccolta è valida e vuota rispetto a tale raccolta, la sezione risultante è una raccolta vuota.

Nota: Una conseguenza di quanto sopra è che un Range valore valido e vuoto rispetto a zero length può essere usato per sezionare una raccolta vuota e comporta una sezione vuota. Ciò è diverso dall'indicizzazione che genera un'eccezione se la raccolta è vuota. nota finale*

Esempio

Uso delle variabili definite in precedenza con 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

Rangeimplementa e i valori possono essere confrontati per l'uguaglianza in base al valore astratto; due IEquatable<Range> valori sono uguali se e solo se i valori astratti Range delle rispettive Start proprietà sono End uguali (§18.2). Tuttavia Range , i valori non vengono ordinati e non vengono fornite altre operazioni di confronto.

Nota:Range i valori non sono ordinati sia come sono astratti che non esiste alcuna relazione di ordinamento univoca. Una volta convertito in un inizio e una lunghezza concrete, ad esempio GetOffsetAndLength, è possibile definire una relazione di ordinamento. nota finale

Range I valori possono essere usati direttamente nella argument_list di un'espressione element_access (§12.8.12), ovvero:

  • un accesso a una matrice e la destinazione è una matrice unidimensionale (§12.8.12.2);
  • accesso a stringhe (§12.8.12.3);
  • un accesso all'indicizzatore e il tipo di destinazione dispone di un indicizzatore con parametri corrispondenti di Range tipo (§12.8.12.4) o di un tipo a cui Range i valori sono convertibili in modo implicito; o
  • un accesso indicizzatore (§12.8.12.4) e il tipo di destinazione è conforme a un modello di sequenza per il quale è specificato il supporto implicito Range (§18.4.3).

Supporto implicito basato su pattern 18.4 per indice e intervallo

18.4.1 Generale

Se un'espressione element_access (§12.8.12) del formato E[A]; dove E ha tipo T ed A è una singola espressione convertibile Index in modo implicito in o Range; non può essere identificata come:

  • accesso a una matrice (§12.8.12.2),
  • accesso a stringhe (§12.8.12.3) o
  • accesso dell'indicizzatore (§12.8.12.4) perché T non fornisce un indicizzatore accessibile adatto

il supporto implicito per l'espressione viene quindi fornito se T è conforme a un modello specifico. Se T non è conforme a questo modello, si verifica un errore in fase di compilazione.

Supporto dell'indice implicito 18.4.2

Se in qualsiasi contesto un'espressione element_access (§12.8.12) del formato E[A]; dove E ha tipo T e A è una singola espressione convertibile in modo implicito in Index; non è valida (§18.4.1) se nello stesso contesto:

  • T fornisce membri accessibili che lo qualificano come sequenza (§18.1); e
  • l'espressione E[0] è valida e usa lo stesso indicizzatore qualificato T come sequenza

quindi l'espressione E[A] deve essere supportata in modo implicito.

Senza limitare altrimenti le implementazioni di questo standard, l'ordine di valutazione dell'espressione deve essere equivalente a:

  1. E viene valutato;
  2. A viene valutato;
  3. la proprietà countable di T viene valutata, se richiesta dall'implementazione;
  4. viene richiamata la funzione di accesso get o set dell'indicizzatore int basato di T che verrebbe usata da E[0] nello stesso contesto.

Supporto dell'intervallo implicito 18.4.3

Se in qualsiasi contesto un'espressione element_access (§12.8.12) del formato E[A]; dove E ha tipo T e A è una singola espressione convertibile in modo implicito in Range; non è valida (§18.4.1) se nello stesso contesto:

  • T fornisce membri accessibili che lo qualificano sia come conteggiabile che come sezionabile (§18.1)

quindi l'espressione E[A] deve essere supportata in modo implicito.

Senza limitare altrimenti le implementazioni di questo standard, l'ordine di valutazione dell'espressione deve essere equivalente a:

  1. E viene valutato;
  2. A viene valutato;
  3. la proprietà countable di T viene valutata, se richiesta dall'implementazione;
  4. viene richiamato il Slice metodo di T .