Compartir a través de


18 Indexación extendida y segmentación

18.1 General

Esta cláusula presenta un modelo para los tipos de colecciónindexables y segmentables extendidos basados en:

  • Los tipos introducidos en esta cláusula (System.Index§18.2) y System.Range (§18.3);
  • Operadores unarios ^ predefinidos (§12.9.6) y binarios .. (§12.10); y
  • Expresión element_access .

En el modelo, un tipo se clasifica como:

  • una colección si representa un grupo de elementos
  • una colección indizable extendida si admite una expresión de element_access que tiene una expresión de argumento único de tipo Index que devuelve o establece un único elemento del tipo, ya sea por valor o por referencia; y
  • una colección segmentable extendida si admite una expresión element_access que tiene una expresión de argumento único de tipo Range que devuelve un segmento de los elementos del tipo por valor.

Nota: El modelo no requiere que se pueda establecer un segmento del tipo, pero un tipo puede admitirlo como una extensión del modelo. nota final

El modelo se admite para matrices unidimensionales (§12.8.12.2) y cadenas (§12.8.12.3).

El modelo puede ser compatible con cualquier clase, estructura o tipo de interfaz que proporcione indizadores adecuados (§15.9) que implementen la semántica del modelo.

Se proporciona compatibilidad implícita para el modelo para los tipos que no lo admiten directamente, pero que proporcionan un determinado patrón de miembros (§18.4). Esta compatibilidad se basa en patrones, en lugar de en semántica, ya que se asume la semántica de los miembros de tipo en los que se basa: el lenguaje no aplica ni comprueba la semántica de estos miembros de tipo.

Para los fines de esta cláusula se definen los siguientes términos:

  • Una colección es un tipo que representa un grupo de elementoss.
  • Una colección con recuento es una que proporciona una propiedad countable una intpropiedad de instancia con valores cuyo valor es el número de elementos que se encuentran actualmente en el grupo. Esta propiedad se denominará o LengthCount. El primero se elige si ambos existen.
  • Un tipo indizable o secuencia es una colección:
    • que es countable;
    • donde se puede tener acceso a cada elemento mediante una expresión element_access con un único argumento requerido int , se permiten los argumentos opcionales adicionales del índice desde el inicio;
    • una secuencia es modificable si todos los elementos también se pueden establecer mediante una expresión element_access ;
    • El índice de inicio de un elemento es el número de elementos antes que en la secuencia, para una secuencia que contiene N elementos:
      • los primeros y últimos elementos tienen índices de 0 y N-1 respectivamente, y
      • el índice de extremo anterior, un índice que representa un elemento hipotético después del último, tiene el valor N.
  • Un índice de un extremo representa la posición de un elemento dentro de una secuencia relativa al índice de extremo anterior. Para una secuencia que contiene N elementos, los índices de primer, último y final son N, 1 y 0 respectivamente.
  • Un intervalo es una ejecución contigua de cero o más índices a partir de cualquier índice dentro de una secuencia.
  • Un segmento es la colección de elementos dentro de un intervalo.
  • Una colección segmentable es una que:
    • es countable;
    • proporciona un método Slice que toma dos int parámetros que especifican un intervalo, siendo un índice inicial y un recuento de elementos respectivamente, y devuelve un nuevo segmento construido a partir de los elementos del intervalo.

Las definiciones anteriores se extienden para usos de Index y Range de la manera siguiente:

  • Un tipo también es una secuencia si se admite una expresión element_access que toma un único argumento necesario Index , en lugar de un int argumento. Cuando se requiere una distinción, el tipo se denomina indexable extendido.
  • Un tipo también se puede segmentar si se admite una expresión element_access que toma un único argumento necesario Range , en lugar de un Slice método. Cuando se requiere una distinción, el tipo se denomina segmentable extendido.

Si un tipo se clasifica como countable, indexable o segmentable está sujeto a las restricciones de accesibilidad de miembros (§7.5) y, por tanto, depende de dónde se use el tipo.

Ejemplo: un tipo en el que la propiedad countable o el indexador son protected solo una secuencia para los miembros de sí mismos y cualquier tipo derivado. ejemplo final

Los miembros necesarios para que un tipo se califique como una secuencia o segmentación se pueden heredar.

Ejemplo: en el código siguiente

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

El tipo A es countable, B es una secuencia y C se puede segmentar y una secuencia.

ejemplo final

Nota:

  • Un tipo se puede segmentar sin ser indexable debido a la falta de un indexador (accesible).
  • Para que un tipo sea segmentable o indizable, es necesario contar el tipo.
  • Aunque los elementos de una secuencia se ordenan por posición dentro de la secuencia, los propios elementos no deben ordenarse por su valor ni siquiera ordenarlos.

nota final

18.2 El tipo de índice

El System.Index tipo representa un índice abstracto que representa un índice desde el inicio o un índice desde el final.

    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 los valores se construyen a partir de , intespecificando el desplazamiento no negativo y un bool, que indica si el desplazamiento es desde el final (true) o el inicio (false). Si el desplazamiento especificado es negativo, se produce una ArgumentOutOfRangeException excepción .

Ejemplo

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

ejemplo final

Hay una conversión implícita de a int la Index que genera índices de inicio y un operador ^ unario definido por el lenguaje (§12.9.6) desde el int que genera índices desde Index el final.

Ejemplo

Con conversiones implícitas y el operador unario ^ se pueden escribir los ejemplos anteriores:

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

ejemplo final

El método GetOffset convierte de un valor abstracto Index a un valor de índice concreto int para una secuencia del especificado length. Si el Index valor , Ies del extremo , este método devuelve el mismo valor length - I.Valueque , de lo contrario, devuelve el mismo valor que I.Value.

Este método no comprueba que el valor devuelto está en el intervalo válido de a través 0 de length-1 inclusive.

Nota: No se especifica ninguna comprobación, ya que el uso esperado del resultado es indexar en una secuencia con length elementos y se espera que la operación de indexación realice las comprobaciones adecuadas. nota final

Indeximplementaciones y valores se pueden comparar para la igualdad en función del valor abstracto; dos IEquatable<Index> valores son iguales Index si y solo si las propiedades y Value respectivas IsFromEnd son iguales. Sin embargo Index , no se ordenan los valores y no se proporciona ninguna otra operación de comparación.

Nota:Index Los valores no están ordenados, ya que son índices abstractos, es en general imposible determinar si un índice desde el extremo viene antes o después de un índice de inicio sin referencia a una longitud de secuencia. Una vez convertidos en índices concretos, por ejemplo, por GetOffset, esos índices concretos son comparables. nota final

Index Los valores se pueden usar directamente en el argument_list de una expresión de element_access (§12.8.12), que es:

  • un acceso de matriz y el destino es una matriz unidimensional (§12.8.12.2);
  • acceso a cadenas (§12.8.12.3)
  • un acceso de indexador y el tipo de destino tiene un indexador con los parámetros correspondientes de cualquier Index tipo (§12.8.12.4) o de un tipo al que Index se pueden convertir implícitamente los valores; o
  • un acceso de indexador y el tipo de destino se ajusta a un patrón de secuencia para el que se especifica compatibilidad implícita Index (§18.4.2).

18.3 El tipo de rango

El System.Range tipo representa el intervalo abstracto de es de Indexun Start índice hasta, pero no incluido, un 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 los valores se construyen a partir de dos Index valores.

Ejemplo

En los ejemplos siguientes se usa la conversión implícita de int a Index (§18.2) y el ^ operador (§12.9.6) para crear los 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`

ejemplo final

El operador .. definido por el lenguaje (§12.10) crea un Range valor a partir de Index valores.

Ejemplo

Con el operador se pueden escribir los .. ejemplos anteriores:

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`

ejemplo final

Los operandos de .. son opcionales 0, el primer valor predeterminado es , el segundo valor predeterminado es ^0.

Ejemplo

Se pueden acortar cinco de los ejemplos anteriores si se basan en valores predeterminados 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`

ejemplo final

Un Range valor es válido con respecto a una longitud L si:

  • los índices concretos con respecto a L de las Range propiedades Start y End están en el intervalo de 0 a L; y
  • el índice concreto de Start no es mayor que el índice concreto para End

El método GetOffsetAndLength con un argumento length convierte un valor abstracto Range en un valor concreto Range representado por tupla. Si el objeto Range no es válido con respecto al length método produce ArgumentOutOfRangeException.

La tupla de hormigón Range devuelta es un par de la forma (S, N) donde:

  • S es el desplazamiento inicial del intervalo, siendo el índice concreto para la Start propiedad de Range; y
  • N es el número de elementos del rango, siendo la diferencia entre los índices concretos de las End propiedades y Start ;
  • ambos valores que se calculan con respecto a length.

Un valor de intervalo concreto está vacío si N es cero. Un intervalo de hormigón vacío puede tener un S valor igual al índice de extremo pasado concreto (§18.1), es posible que un intervalo no vacío no sea vacío. Range Cuando se usa para segmentar (§18.1), una colección es válida y está vacía con respecto a esa colección, el segmento resultante es una colección vacía.

Nota: Una consecuencia de lo anterior es que un Range valor válido y vacío con respecto a un length de cero se puede usar para segmentar una colección vacía y da como resultado un segmento vacío. Esto difiere de la indexación que produce una excepción si la colección está vacía. nota final*

Ejemplo

Uso de las variables definidas anteriormente 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

Rangeimplementaciones y valores se pueden comparar para la igualdad en función del valor abstracto; dos IEquatable<Range> valores son iguales si y solo si los valores abstractos Range de las propiedades respectivas Start y End son iguales (§18.2). Sin embargo Range , no se ordenan los valores y no se proporciona ninguna otra operación de comparación.

Nota:Range los valores no están ordenados, ya que son abstractos y no hay ninguna relación de ordenación única. Una vez convertido en un inicio y una longitud concretos, por ejemplo, por GetOffsetAndLength, se podría definir una relación de ordenación. nota final

Range Los valores se pueden usar directamente en el argument_list de una expresión de element_access (§12.8.12), que es:

  • un acceso de matriz y el destino es una matriz unidimensional (§12.8.12.2);
  • acceso a cadenas (§12.8.12.3);
  • un acceso de indexador y el tipo de destino tiene un indexador con los parámetros correspondientes de cualquier Range tipo (§12.8.12.4) o de un tipo al que Range se pueden convertir implícitamente los valores; o
  • un acceso de indexador (§12.8.12.4) y el tipo de destino se ajusta a un patrón de secuencia para el que se especifica compatibilidad implícita Range (§18.4.3).

18.4 Compatibilidad implícita basada en patrones para Index y Range

18.4.1 General

Si una expresión element_access (§12.8.12) del formulario E[A]; donde E tiene el tipo T y A es una expresión única que se puede convertir implícitamente en Index o Range; no se puede identificar como:

  • acceso a una matriz (§12.8.12.2),
  • acceso a cadenas (§12.8.12.3) o
  • un acceso al indexador (§12.8.12.4), ya T que no proporciona ningún indexador accesible adecuado

a continuación, se proporciona compatibilidad implícita con la expresión si T se ajusta a un patrón determinado. Si T no se ajusta a este patrón, se produce un error en tiempo de compilación.

Compatibilidad con índices implícitos 18.4.2

Si en cualquier contexto, una expresión element_access (§12.8.12) del formulario E[A]; donde E tiene el tipo T y A es una expresión única que se puede convertir implícitamente en Index; no es válida (§18.4.1), si está en el mismo contexto:

  • T proporciona a los miembros accesibles que lo califican como una secuencia (§18.1); y
  • la expresión E[0] es válida y usa el mismo indexador que se T califica como una secuencia.

después, la expresión E[A] se admitirá implícitamente.

Sin restringir de otra manera las implementaciones de este estándar, el orden de evaluación de la expresión será equivalente a:

  1. E se evalúa;
  2. A se evalúa;
  3. Se evalúa la propiedad de recuento de T , si es necesario por la implementación;
  4. Se invoca el descriptor de acceso get o set del int indexador basado de T que usaría E[0] en el mismo contexto.

Compatibilidad con intervalos implícitos 18.4.3

Si en cualquier contexto, una expresión element_access (§12.8.12) del formulario E[A]; donde E tiene el tipo T y A es una expresión única que se puede convertir implícitamente en Range; no es válida (§18.4.1), si está en el mismo contexto:

  • T proporciona a los miembros accesibles que lo califican como recuentos y segmentables (§18.1)

después, la expresión E[A] se admitirá implícitamente.

Sin restringir de otra manera las implementaciones de este estándar, el orden de evaluación de la expresión será equivalente a:

  1. E se evalúa;
  2. A se evalúa;
  3. Se evalúa la propiedad de recuento de T , si es necesario por la implementación;
  4. se invoca el Slice método de T .