Поделиться через


18 Расширенный индексирование и срез

18.1 Общие

В этом предложении представлена модель для расширенных индексируемых и срезаемых типов коллекций, созданных на основе:

  • Типы, представленные в этом предложении, System.Index (§18.2) и System.Range (§18.3);
  • Предопределенные унарные ^ операторы (§12.9.6) и двоичные .. (§12.10) и
  • Выражение element_access .

В модели тип классифицируется следующим образом:

  • Коллекция, если она представляет группу элементов
  • расширенная индексируемая коллекция, если она поддерживает выражение element_access, которое имеет одно выражение аргумента типа, которое возвращает и/или задает один элемент типаIndex, по значению или по ссылке;
  • Расширенная срезаемая коллекция, если она поддерживает выражение element_access, которое имеет одно выражение аргумента типаRange, которое возвращает срез элементов типа по значению.

Примечание. Модель не требует установки среза типа, но тип может поддерживать его как расширение модели. конечная заметка

Модель поддерживается для одномерных массивов (§12.8.12.2) и строк (§12.8.12.3).

Модель может поддерживаться любым классом, структурой или типом интерфейса, который предоставляет соответствующие индексаторы (§15.9), реализующие семантику модели.

Неявная поддержка модели предоставляется для типов, которые не поддерживают его напрямую, но предоставляют определенный шаблон элементов (§18.4). Эта поддержка основана на шаблонах, а не на основе семантики, так как семантика элементов типа, на которых она основана, — язык не применяется или проверяет семантику этих элементов типа.

В целях этого предложения определены следующие термины:

  • Коллекция — это тип, представляющий группу элементов.
  • Подсчитываемая коллекция — это коллекция, которая предоставляет подсчитываемое свойство экземпляра с intзначением, значение которого — это количество элементов, которые в настоящее время находятся в группе. Это свойство должно быть названо либо Length или Count. Первый выбирается, если оба существуют.
  • Последовательность или индексируемый тип — это коллекция:
    • значение, которое можно подсчитать;
    • где доступ к каждому элементу можно получить с помощью выражения element_access с одним обязательным int аргументом, индексом от начала разрешены дополнительные необязательные аргументы;
    • последовательность модификируется , если каждый элемент также можно задать с помощью выражения element_access ;
    • Индекс от начала элемента — это число элементов перед ним в последовательности для последовательности, содержащей N-элементы :
      • первые и последние элементы имеют индексы 0 и N-1 соответственно, и
      • прошлый индекс, индекс, представляющий гипотетический элемент после последнего, имеет значение N.
  • Индекс из конца представляет позицию элемента в последовательности относительно последнего индекса. Для последовательности, содержащей N-элементы , первые, последние и конечные индексы: N, 1 и 0 соответственно.
  • Диапазон представляет собой последовательный запуск нулевых или более индексов, начиная с любого индекса в последовательности.
  • Срез — это коллекция элементов в диапазоне.
  • Срезная коллекция — это одна из следующих элементов:
    • имеет значение countable;
    • предоставляет метод Slice , который принимает два int параметра, указывающие диапазон, будучи начальным индексом и числом элементов соответственно, и возвращает новый срез, созданный из элементов в диапазоне.

Приведенные выше определения расширены для использования Index и Range следующим образом:

  • Тип также является последовательностью , если выражение element_access принимает один обязательный Index аргумент, а не int аргумент, поддерживается. Если требуется различие, тип называется расширенным индексируемым.
  • Тип также можно срезать , если выражение element_access принимает один обязательный Range аргумент, а не Slice метод. Если требуется различие, тип называется расширенным срезом.

Классифицируется ли тип как подсчитываемый, индексируемый или срез, зависит от ограничений специальных возможностей членов (§7.5) и, следовательно, зависит от того, где используется тип.

Пример. Тип, в котором вычисляемое свойство и /или индексатор является protected только последовательностью для членов себя и любых производных типов. заключительный пример

Обязательные элементы для типа, квалифицируемые как последовательность или срез, могут быть унаследованы.

пример: в следующем коде

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

Тип A подсчитывается, B является последовательностью и C срезом и последовательностью.

заключительный пример

Примечание.

  • Тип может быть срезан без индексации из-за отсутствия (доступного) индексатора.
  • Чтобы тип был срезаемым и (или) индексируемым, требуется, чтобы тип был подсчитываемым.
  • Хотя элементы последовательности упорядочены по позиции в последовательности, сами элементы не должны быть упорядочены по их значению или даже упорядоченным.

конечная заметка

18.2 Тип индекса

Тип System.Index представляет абстрактный индекс, представляющий либо индекс из начала, либо индекс из конца.

    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 значения создаются из int, указывая не отрицательное смещение и boolзначение, указывающее, является ли смещение от конца (true) или запуска (false). Если указанное смещение является отрицательным ArgumentOutOfRangeException , создается.

Пример

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

заключительный пример

Существует неявное преобразование, intIndex из которого производится от начала индексов, и определяемый языком унарный оператор ^ (§12.9.6), из intIndex которого производится из конечных индексов.

Пример

С помощью неявных преобразований и унарного ^ оператора, приведенных выше, можно записать:

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

заключительный пример

Метод GetOffset преобразуется из абстрактного Index значения в конкретное int значение индекса для последовательности указанного значения length. Если значение, Indexто в I конце этого метода возвращается то же значение, что и в противном случае возвращается то же значение, что length - I.ValueI.Valueи .

Этот метод не проверяет, находится ли возвращаемое значение в допустимом диапазоне 0 включительно length-1 .

Заметка: Проверка не указана, так как ожидаемое использование результата заключается в индексировании в последовательности с length элементами, и эта операция индексирования, как ожидается, выполняет соответствующие проверки. конечная заметка

Index IEquatable<Index> реализации и значения можно сравнить для равенства на основе абстрактного значения; два Index значения равны, если соответствующие Value и IsFromEnd свойства равны. Однако Index значения не упорядочены и другие операции сравнения не предоставляются.

Заметка:Index Значения не упорядочены по мере того, как они являются абстрактными индексами, обычно невозможно определить, приходит ли индекс из конца до или после начального индекса без ссылки на длину последовательности. После преобразования в конкретные индексы, например, GetOffsetэти конкретные индексы сопоставимы. конечная заметка

Index значения могут использоваться непосредственно в argument_list выражения element_access (§12.8.12), т. е.

  • доступ к массиву и целевой объект является одномерным массивом (§12.8.12.2);
  • строковый доступ (§12.8.12.3)
  • доступ индексатора и целевой тип имеет индексатор с соответствующими параметрами любого Index типа (§12.8.12.4) или типа, к которому Index значения неявно преобразуются; или
  • доступ индексатора и целевой тип соответствуют шаблону последовательности, для которой указана неявная Index поддержка (§18.4.2).

18.3 Тип диапазона

Тип System.Range представляет абстрактный диапазон Indexes от Start индекса до индекса, но не включая 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 значения создаются из двух Index значений.

Пример

В следующих примерах используется неявное преобразование из intIndex (§18.2) и ^ оператор (§12.9.6) для создания Index значений для каждого 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`

заключительный пример

Определяемый языком оператор .. (§12.10) создает Range значение из Index значений.

Пример

С помощью оператора, приведенного .. выше, можно написать следующее:

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`

заключительный пример

Операнды .. являются необязательными, первые значения по умолчанию используются по умолчанию0^0.

Пример

Пять из приведенных выше примеров можно сократить, опираясь на значения по умолчанию для операндов:

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`

заключительный пример

Значение Rangeдопустимо в отношении длины L , если:

  • конкретные индексы относительно LRange свойств Start и End находятся в диапазоне от 0 до L; и
  • конкретный индекс не превышает конкретный индекс Start для End

Метод GetOffsetAndLength с аргументом length преобразует абстрактное Range значение в конкретное Range значение, представленное кортежем. Если недопустимый Rangelength метод вызывает ArgumentOutOfRangeExceptionисключение.

Возвращаемый бетонный Range кортеж является парой формы (S, N) , в которой:

  • S — начальное смещение диапазона, являясь конкретным индексом для Start свойства объекта Range; и
  • N — это количество элементов в диапазоне, то есть разница между конкретными индексами для End и Start свойств;
  • оба значения вычисляются относительно length.

Значение конкретного диапазона пусто , если N равно нулю. Пустой S диапазон бетона может иметь значение, равное конкретному индексу в прошлом (§18.1), непустый диапазон может не совпадать. Когда коллекция Range используется для среза (§18.1) является допустимой и пустой в отношении этой коллекции, то результирующий срез является пустой коллекцией.

Заметка: Следствием приведенного выше является то, что Range допустимое и пустое значение в отношении length нуля может использоваться для среза пустой коллекции и приводит к пустому срезу. Это отличается от индексирования, которое создает исключение, если коллекция пуста. конечная заметка*

Пример

Использование переменных, определенных выше, с 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 IEquatable<Range> реализации и значения можно сравнить для равенства на основе абстрактного значения; два Range значения равны, если абстрактные значения соответствующих Start и End свойств равны (§18.2). Однако Range значения не упорядочены и другие операции сравнения не предоставляются.

Заметка:Range значения не упорядочены как абстрактные, так и нет уникального отношения упорядочивания. После преобразования в бетонное начало и длину, например GetOffsetAndLengthпутем упорядочения, можно определить отношение упорядочения. конечная заметка

Range значения можно использовать непосредственно в argument_list выражения element_access (§12.8.12), которое:

  • доступ к массиву и целевой объект является одномерным массивом (§12.8.12.2);
  • строковый доступ (§12.8.12.3);
  • доступ индексатора и целевой тип имеет индексатор с соответствующими параметрами любого Range типа (§12.8.12.4) или типа, к которому Range значения неявно преобразуются; или
  • доступ индексатора (§12.8.12.4) и целевой тип соответствует шаблону последовательности, для которой указана неявная Range поддержка (§18.4.3).

Неявная поддержка индексов и диапазонов на основе шаблонов 18.4

18.4.1 Общие

Если выражение element_access (§12.8.12) формы E[A], где E имеет тип T и A является неявным Index преобразованием одного выражения в или Range; не удается определить как:

  • доступ к массиву (§12.8.12.2),
  • строковый доступ (§12.8.12.3) или
  • доступ индексатора (§12.8.12.4), так как T не предоставляет подходящий индексатор.

Затем неявная поддержка выражения предоставляется, если T соответствует определенному шаблону. Если T этот шаблон не соответствует этому шаблону, возникает ошибка во время компиляции.

Поддержка неявного индекса 18.4.2

Если в любом контексте выражение element_access (§12.8.12) формы E[A]; где E имеет тип T и A является неявным преобразуемым Indexвыражением; недопустимо (§18.4.1), то в том же контексте:

  • T предоставляет доступные члены, квалифицируя его как последовательность (§18.1); и
  • Выражение E[0] допустимо и использует тот же индексатор, который квалифифисируется T как последовательность

затем выражение E[A] должно быть неявно поддерживается.

Без ограничения реализаций этого стандарта порядок оценки выражения должен быть эквивалентен следующим:

  1. E вычисляется;
  2. A вычисляется;
  3. вычисляемое число свойств T , если требуется реализацией;
  4. Вызывается метод доступа к индексатору intT на основе объекта get или set, который будет использоваться E[0] в том же контексте.

Поддержка неявного диапазона 18.4.3

Если в любом контексте выражение element_access (§12.8.12) формы E[A]; где E имеет тип T и A является неявным преобразуемым Rangeвыражением; недопустимо (§18.4.1), то в том же контексте:

  • Tпредоставляет доступные члены, квалифицируемые и срезаемые (§18.1)

затем выражение E[A] должно быть неявно поддерживается.

Без ограничения реализаций этого стандарта порядок оценки выражения должен быть эквивалентен следующим:

  1. E вычисляется;
  2. A вычисляется;
  3. вычисляемое число свойств T , если требуется реализацией;
  4. Slice Вызывается методT.