Partilhar via


18 Indexação e fatiamento estendidos

18.1 Generalidades

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

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

No modelo, um tipo é classificado como:

  • uma coleção se representar um grupo de elementoss
  • uma coleção indexável estendida se suportar 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, seja por valor ou por referência;
  • Uma coleção fatiável estendida se ela 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.

Nota: O modelo não requer que uma fatia do tipo possa ser definida, mas um tipo pode suportá-lo como uma extensão do modelo. Nota final

O modelo é suportado para matrizes unidimensionais (§12.8.12.2) e strings (§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 apoio implícito ao modelo é fornecido para tipos que não o apoiam diretamente, mas que fornecem um determinado padrão de membros (§18.4). Esse suporte é baseado em padrões, em vez de baseado em semântica, pois a semântica dos membros do tipo nos quais ele se baseia é assumida – a linguagem não impõe, ou verifica, a semântica desses membros do tipo.

Para efeitos da presente cláusula, entende-se por:

  • Uma coleção é um tipo que representa um grupo de elementoss.
  • Uma coleção contável é aquela que fornece uma propriedade contável , uma intpropriedade de instância com valor valorizado cujo valor é o número de elementos atualmente no grupo. Esta propriedade deve ser denominada LengthCountou . O primeiro é escolhido se ambos existirem.
  • Uma sequência ou tipo indexável é uma coleção:
    • que é contável;
    • onde 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 é modificável se cada elemento também puder ser definido usando uma expressão element_access ;
    • O índice from-start de um elemento é o número de elementos antes dele na sequência, para uma sequência que contém N elementos:
      • o primeiro e o último elementos têm índices de 0 e N-1, respectivamente, e
      • o índice passado-final, um índice que representa um elemento hipotético após o último, tem o valor N.
  • Um índice from-end representa a posição de um elemento dentro de uma sequência relativa ao índice passado-final. Para uma sequência que contém elementos N , o primeiro, o último e o índice passado-final são N, 1 e 0, respectivamente.
  • Um intervalo é uma série contígua de zero ou mais índices que começam em qualquer índice dentro de uma sequência.
  • Uma fatia é a coleção de elementos dentro de um intervalo.
  • Uma coleção fatiá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 alargadas às utilizações de Index e Range do seguinte modo:

  • Um tipo também é uma sequência se uma expressão element_access usando um único argumento necessário Index , em vez de um int argumento, for suportada. Quando é necessária uma distinção, o tipo é denominado indexável alargado.
  • Um tipo também é fatiável se uma expressão element_access usando um único argumento necessário Range , em vez de um Slice método, for suportada. Quando é necessária uma distinção, o tipo é denominado fatiável alargado.

Se um tipo é classificado como contável, indexável ou fatiável está sujeito às restrições de acessibilidade do membro (§7.5) e, portanto, depende de onde o tipo está sendo usado.

Exemplo: Um tipo onde a propriedade contável e/ou o indexador são protected é apenas uma sequência para membros de si mesmo e quaisquer tipos derivados. Exemplo final

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

Exemplo: No seguinte código

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 é fatiável e uma sequência.

Exemplo final

Observação:

  • Um tipo pode ser fatiado sem ser indexável devido à falta de um indexador (acessível).
  • Para que um tipo seja fatiável e/ou indexável é necessário que o tipo seja contável.
  • Enquanto os elementos de uma sequência são ordenados por posição dentro da sequência, os elementos em si não precisam ser ordenados pelo seu valor, ou mesmo ordenáveis.

Nota final

18.2 O tipo de índice

O System.Index tipo representa um índice abstrato que representa ou um índice inicial ou um índice do fim.

    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á lançado.

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

Exemplo final

Há uma conversão implícita a int partir da qual produz índices a partir do Index início, e um operador ^ unário definido pela linguagem (§12.9.6) a int partir do Index qual produz índices a partir do fim.

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

Exemplo final

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

Este método não verifica se o valor de retorno está no intervalo válido de 0 through length-1 inclusive.

Observação: Nenhuma verificação é especificada, pois o uso esperado do resultado é indexar em uma sequência com length elementos, e essa operação de indexação deve executar 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 os respetivos Value e IsFromEnd propriedades forem iguais. Index No entanto, os valores não são ordenados e nenhuma outra operação de comparação é fornecida.

Observação: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 exemplo, por GetOffset, esses índices concretos são comparáveis. Nota final

Index Os valores podem ser utilizados diretamente na argument_list de uma expressão element_access (§12.8.12) que seja:

  • um acesso à matriz e o alvo é uma matriz unidimensional (§12.8.12.2);
  • um acesso à cadeia de caracteres (§12.8.12.3)
  • um acesso 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 convertíveis;
  • Um acesso indexador e o tipo de destino estão em conformidade com um padrão de sequência para o qual é especificado suporte implícito Index (§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`

Exemplo final

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

Exemplo

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

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`

Exemplo final

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

Exemplo

Cinco dos exemplos acima podem ser encurtados confiando em 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`

Exemplo final

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

  • os índices de concreto em relação a L das Range propriedades Start e End estão na faixa de 0 a L;
  • o índice de betão para Start não é superior ao índice de betão para End

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

A tupla de concreto Range devolvido é um par da forma (S, N) onde:

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

Um valor de intervalo concreto está vazio se N for zero. Um intervalo de betão vazio pode ter um S valor igual ao índice passado-final do betão (§18.1), um intervalo não vazio não pode. Quando uma Range coleção que é usada para fatiar (§18.1) uma coleção é válida e vazia em relação a essa coleção, então a fatia resultante é uma coleção vazia.

Observação: Uma consequência do acima exposto é que um Range valor válido e vazio em relação a um length de zero pode ser usado para fatiar uma coleção vazia e resulta em uma fatia vazia. Isso difere da indexação, que lança 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 respetivas Start propriedades e End forem iguais (§18.2). Range No entanto, os valores não são ordenados e nenhuma outra operação de comparação é fornecida.

Observação:Range os valores não são ordenados, pois são abstratos e não há uma relação de ordenação única. Uma vez convertido em um início e comprimento concretos, por exemplo, por GetOffsetAndLength, uma relação de ordenação pode ser definida. Nota final

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

  • um acesso à matriz e o alvo é uma matriz unidimensional (§12.8.12.2);
  • um acesso por cadeia de caracteres (§12.8.12.3);
  • um acesso 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 convertíveis;
  • um acesso indexador (§12.8.12.4) e o tipo de destino está em conformidade com um padrão de sequência para o qual é especificado suporte implícito Range (§18.4.3).

18.4 Suporte implícito baseado em padrões para índice e intervalo

18.4.1 Generalidades

Se uma expressão element_access (§12.8.12) do formulário E[A]; em que E tem tipo T e A é uma única expressão implicitamente convertível em Index ou Range; não for identificada como:

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

em seguida, o suporte implícito para a expressão é fornecido se T estiver em conformidade com um padrão particular. Se T não estiver de acordo com este padrão, ocorrerá um erro em tempo de compilação.

18.4.2 Suporte de índice implícito

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

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

nesse caso, a expressão E[A] deve ser implicitamente apoiada.

Sem restringir de outra forma as implementações desta Norma, a ordem de avaliação da expressão será equivalente a:

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

18.4.3 Suporte implícito de intervalo

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

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

nesse caso, a expressão E[A] deve ser implicitamente apoiada.

Sem restringir de outra forma as implementações desta Norma, a ordem de avaliação da expressão será equivalente a:

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