Compartilhar via


18 Indexação e fatiamento estendidos

18.1 Geral

Esta cláusula introduz um modelo para tipos de coleçãoindexáveis e segmentáveis estendidos baseados em:

  • Os tipos introduzidos nesta cláusula, System.Index (§18.2) e System.Range (§18.3);
  • Os operadores unários ^ predefinidos (§12.9.6) e binários .. (§12.10) e
  • A expressão element_access .

No modelo, um tipo é classificado como:

  • uma coleção se ela representa um grupo de elementoss
  • uma coleção indexável estendida se oferecer suporte a uma expressão element_access que tenha uma única expressão de argumento do tipo Index que retorna e/ou define um único elemento do tipo, por valor ou por referência; e
  • uma coleção segmentável estendida se oferecer suporte a uma expressão element_access que tenha uma única expressão de argumento do tipo Range que retorna uma fatia dos elementos do tipo por valor.

Observação: o modelo não exige que uma fatia do tipo possa ser definida, mas um tipo pode dar suporte a ele como uma extensão do modelo. nota final

O modelo tem suporte para matrizes unidimensionais (§12.8.12.2) e cadeias de caracteres (§12.8.12.3).

O modelo pode ser suportado por qualquer classe, struct ou tipo de interface que forneça indexadores apropriados (§15.9) que implementem a semântica do modelo.

O suporte implícito para o modelo é fornecido para tipos que não dão suporte diretamente a ele, mas que fornecem um determinado padrão de membros (§18.4). Esse suporte é baseado em padrão, em vez de baseado em semântica, como a semântica dos membros do tipo nos quais se baseia são assumidas – o idioma não impõe ou verifica a semântica desses membros de tipo.

Para os fins desta cláusula, os seguintes termos são definidos:

  • Uma coleção é um tipo que representa um grupo de elementos.
  • Uma coleção que pode ser contada é aquela que fornece uma propriedade de instância cujo valor é o número de elementos atualmente no grupo. Essa propriedade deve ser nomeada Length ou Count. O primeiro será escolhido se ambos existirem.
  • Uma sequência ou tipo indexável é uma coleção:
    • que é contível;
    • em que cada elemento pode ser acessado usando uma expressão element_access com um único argumento necessário int , o índice de início, argumentos opcionais adicionais são permitidos;
    • uma sequência será modificável se cada elemento também puder ser definido usando uma expressão element_access ;
    • O índice de um elemento desde o início é o número de elementos antes dele na sequência, para uma sequência que contém N elementos:
      • os primeiros e últimos elementos têm índices de 0 e N-1, respectivamente, e
      • o índice de última extremidade, um índice que representa um elemento hipotético após o último, tem o valor N.
  • Um índice de ponta representa a posição de um elemento dentro de uma sequência relativa ao índice de última hora. Para uma sequência que contém N elementos, os índices primeiro, último e último são N, 1 e 0, respectivamente.
  • Um intervalo é uma execução contígua de zero ou mais índices começando em qualquer índice dentro de uma sequência.
  • Uma fatia é a coleção de elementos dentro de um intervalo.
  • Uma coleção segmentável é aquela que:
    • é contível;
    • fornece um método Slice que usa dois int parâmetros especificando um intervalo, sendo um índice inicial e uma contagem de elementos, respectivamente, e retorna uma nova fatia construída a partir dos elementos no intervalo.

As definições acima são estendidas para usos de e Index da Range seguinte maneira:

  • Um tipo também será uma sequência se uma expressão element_access que usa um único argumento necessário Index , em vez de um int argumento, tiver suporte. Quando uma distinção é necessária, o tipo é denominado indexável estendido.
  • Um tipo também será fatiado se uma expressão element_access que usa um único argumento necessário Range , em vez de um Slice método, tiver suporte. Quando uma distinção é necessária, o tipo é denominado segmentável estendido.

Se um tipo é classificado como contagem, indexável ou segmentável está sujeito às restrições de acessibilidade de membro (§7,5) e, portanto, dependente de onde o tipo está sendo usado.

Exemplo: um tipo em que a propriedade countable e/ou o indexador são protected apenas uma sequência para membros de si mesmo e de qualquer tipo derivado. fim de exemplo

Os membros necessários para um tipo se qualificar como uma sequência ou segmentável podem ser herdados.

Exemplo: no código a seguir

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

O tipo A é contível, B é uma sequência e C é segmentável e uma sequência.

fim de exemplo

Note:

  • Um tipo pode ser fatiado sem ser indexável devido à falta de um indexador (acessível).
  • Para que um tipo seja segmentável e/ou indexável, é necessário que o tipo seja contível.
  • Embora os elementos de uma sequência sejam ordenados por posição dentro da sequência, os próprios elementos não precisam ser ordenados por seu valor ou mesmo orderable.

nota final

18.2 O tipo de índice

O System.Index tipo representa um índice abstrato que representa um índice desde o início ou um índice de ponta.

    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 os valores são construídos a partir de um int, especificando o deslocamento não negativo e um bool, indicando se o deslocamento é do final (true) ou do início (false). Se o deslocamento especificado for negativo, um ArgumentOutOfRangeException será gerado.

Exemplo

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

fim de exemplo

Há uma conversão implícita da intIndex qual produz índices de início e um operador ^ unário definido por linguagem (§12.9.6) para int o Index qual produz índices de ponta.

Exemplo

Usando conversões implícitas e o operador unário ^ , os exemplos acima podem ser escritos:

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

fim de exemplo

O método GetOffset converte de um valor abstrato Index em um valor de índice concreto int para uma sequência do especificado length. Se o Index valor for I, será de ponta a ponta, esse método retornará o mesmo valor length - I.Valueque, caso contrário, retornará o mesmo valor que I.Value.

Esse método não verifica se o valor retornado está no intervalo válido de por meio 0 da length-1 inclusão.

Nota: Nenhuma verificação é especificada, pois o uso esperado do resultado é indexar em uma sequência com length elementos, e espera-se que a operação de indexação execute as verificações apropriadas. nota final

Index implementos IEquatable<Index> e valores podem ser comparados para igualdade com base no valor abstrato; dois Index valores são iguais se e somente se as respectivas Value propriedades e IsFromEnd propriedades forem iguais. No entanto Index , os valores não são ordenados e nenhuma outra operação de comparação é fornecida.

Nota:Index os valores não são ordenados, pois são índices abstratos, em geral é impossível determinar se um índice de ponta vem antes ou depois de um índice de início sem referência a um comprimento de sequência. Uma vez convertidos em índices concretos, por GetOffsetexemplo, esses índices concretos são comparáveis. nota final

Index os valores podem ser usados diretamente no argument_list de uma expressão element_access (§12.8.12), que é:

  • um acesso de matriz e o destino é uma matriz unidimensional (§12.8.12.2);
  • um acesso de cadeia de caracteres (§12.8.12.3)
  • um acesso de indexador e o tipo de destino tem um indexador com parâmetros correspondentes de qualquer Index tipo (§12.8.12.4) ou de um tipo para o qual Index os valores são implicitamente conversíveis; ou
  • um acesso de indexador e o tipo de destino estão em conformidade com um padrão de sequência para o qual o suporte implícito Index é especificado (§18.4.2).

18.3 O tipo de intervalo

O System.Range tipo representa o intervalo abstrato de Indexes de um Start índice até, mas não incluindo, um End índice.

    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 os valores são construídos a partir de dois Index valores.

Exemplo

Os exemplos a seguir usam a conversão implícita de int para Index (§18.2) e o ^ operador (§12.9.6) para criar os Index valores para cada 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`

fim de exemplo

O operador .. definido pelo idioma (§12.10) cria um Range valor a partir de Index valores.

Exemplo

Usando o .. operador, os exemplos acima podem ser gravados:

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`

fim de exemplo

Os operandos de .. são opcionais, o primeiro usa como padrão 0, o segundo usa como padrão ^0.

Exemplo

Cinco dos exemplos acima podem ser reduzidos dependendo dos valores padrão para operandos:

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`

fim de exemplo

Um Range valor é válido em relação a um comprimento L se:

  • os índices concretos em relação a L das Range propriedades Start e End estão no intervalo de 0 a L; e
  • o índice concreto para Start não é maior do que o índice concreto para End

O método GetOffsetAndLength com um argumento length converte um valor abstrato Range em um valor concreto Range representado pela tupla. Se o Range método não for válido em relação ao length método será gerado ArgumentOutOfRangeException.

A tupla de concreto Range retornada é um par da forma (S, N) em que:

  • S é o deslocamento inicial do intervalo, sendo o índice concreto para a Start propriedade do Range; e
  • Né o número de itens no intervalo, sendo a diferença entre os índices concretos para as propriedades e End as Start propriedades;
  • ambos os valores que estão sendo calculados em relação a length.

Um valor de intervalo concreto estará vazio se N for zero. Um intervalo de concreto vazio pode ter um S valor igual ao índice de última extremidade concreto (§18.1), um intervalo não vazio pode não ser. Quando um Range que é usado para fatiar (§18.1) uma coleção é válida e vazia em relação a essa coleção, a fatia resultante é uma coleção vazia.

Nota: Uma consequência disso é que um Range valor válido e vazio em relação a um length zero pode ser usado para fatiar uma coleção vazia e resultar em uma fatia vazia. Isso difere da indexação que gera uma exceção se a coleção estiver vazia. nota final*

Exemplo

Usando as variáveis definidas acima com 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 implementos IEquatable<Range> e valores podem ser comparados para igualdade com base no valor abstrato; dois Range valores são iguais se e somente se os valores abstratos das respectivas Start propriedades forem End iguais (§18,2). No entanto Range , os valores não são ordenados e nenhuma outra operação de comparação é fornecida.

Nota:Range os valores não são ordenados, pois são abstratos e não há nenhuma relação de ordenação exclusiva. Uma vez convertido em um início e comprimento concretos, por exemplo, uma GetOffsetAndLengthrelação de ordenação poderia ser definida. nota final

Range os valores podem ser usados diretamente no argument_list de uma expressão element_access (§12.8.12), que é:

  • um acesso de matriz e o destino é uma matriz unidimensional (§12.8.12.2);
  • um acesso de cadeia de caracteres (§12.8.12.3);
  • um acesso de indexador e o tipo de destino tem um indexador com parâmetros correspondentes de qualquer Range tipo (§12.8.12.4) ou de um tipo para o qual Range os valores são implicitamente conversíveis; ou
  • um acesso de indexador (§12.8.12.4) e o tipo de destino está em conformidade com um padrão de sequência para o qual o suporte implícito Range é especificado (§18.4.3).

18.4 Suporte implícito baseado em padrão para Índice e Intervalo

18.4.1 Geral

Se uma expressão element_access (§12.8.12) do formulário E[A]; onde E tem o tipo T e A é uma única expressão implicitamente conversível para Index ou Range; não é identificado como:

  • um acesso de matriz (§12.8.12.2),
  • um acesso de cadeia de caracteres (§12.8.12.3) ou
  • um acesso de indexador (§12.8.12.4) como T não fornece nenhum indexador acessível adequado

em seguida, o suporte implícito para a expressão será fornecido se T estiver em conformidade com um padrão específico. Se T não estiver em conformidade com esse padrão, ocorrerá um erro de tempo de compilação.

Suporte a índice implícito 18.4.2

Se, em qualquer contexto, uma expressão de element_access (§12.8.12) do formulário E[A]; em que E tem o tipo T e A é uma única expressão implicitamente conversível para Index; não é válida (§18.4.1) em seguida, se no mesmo contexto:

  • T fornece membros acessíveis qualificando-o como uma sequência (§18.1); e
  • a expressão E[0] é válida e usa o mesmo indexador que se qualifica T como uma sequência

em seguida, a expressão E[A] deve ter suporte implícito.

Sem restringir as implementações deste Padrão, a ordem de avaliação da expressão será equivalente a:

  1. E é avaliado;
  2. A é avaliado;
  3. a propriedade countable de T é avaliada, se necessário pela implementação;
  4. o acessador get ou set do int indexador T baseado do qual seria usado E[0] no mesmo contexto é invocado.

18.4.3 Suporte a intervalo implícito

Se, em qualquer contexto, uma expressão de element_access (§12.8.12) do formulário E[A]; em que E tem o tipo T e A é uma única expressão implicitamente conversível para Range; não é válida (§18.4.1) em seguida, se no mesmo contexto:

  • T fornece membros acessíveis qualificando-o como contável e fatiado (§18.1)

em seguida, a expressão E[A] deve ter suporte implícito.

Sem restringir as implementações deste Padrão, a ordem de avaliação da expressão será equivalente a:

  1. E é avaliado;
  2. A é avaliado;
  3. a propriedade countable de T é avaliada, se necessário pela implementação;
  4. o Slice método de T é invocado.