ArraySegment должен быть приведен к IList для итерации по нему с индексом.Есть ли способ скрыть ненужные методы IList? - PullRequest
0 голосов
/ 01 октября 2018

Чтобы иметь возможность обрабатывать ArraySegment напрямую, как массив, вы должны привести его к IList с помощью «as».Это описывается как правильное поведение здесь:

использование класса ArraySegment?

и здесь:

dotNet ArraySegment Struct

В частности, документ Microsoft гласит:

Если вы хотите извлечь элемент по его индексу в объекте ArraySegment, вы должны привести его к объекту IList и получить или изменить его.используя свойство IList.Item [Int32].В следующем примере извлекается элемент в объекте ArraySegment, который разделяет раздел массива строк.

Недоумение вызывает то, что в IList есть такие методы, как «Remove» и «RemoveAt».Можно ожидать, что они не будут работать с сегментом массива, приведенным в виде списка.Я проверил это, и на самом деле вызов Remove выдает ошибку времени выполнения.Но компилятор не улавливает проблему.

Я удивлен, что Microsoft сочла это приемлемым поведением при разработке ArraySegment.

Я пытался провести мозговой штурм в классе-обертке или каким-то способом скрыть методы List, которые не должны вызываться в ArraySegment, как List.Но я ничего не смог придумать.

У кого-нибудь есть предложения, как это исправить?

РЕДАКТИРОВАТЬ: вместо IList было предложено IReadOnlyList. IReadOnlyListделает список полностью доступным только для чтения, не позволяя вам изменять значение элементов, хранящихся в базовом массиве.Я хочу иметь возможность изменить исходные значения массива.Я просто не хочу иметь возможность писать list.Remove () или list.Add (), поскольку это явно неправильно и компилятор не должен этого допускать.

Всем, кто может предложитьSpan в качестве альтернативы:

Мне известен Span, но в настоящее время Span имеет ограничения в .NET Framework и Standard.В частности, он может использоваться только как локальная переменная и, следовательно, не может быть передан другим методам.

И, честно говоря, я действительно считаю, что IEnumerable Heirarchy от Microsoft оставляет желать лучшего- Я не могу придумать, как создать индексируемую последовательность, такую ​​как List, без функции добавления / удаления.ICollection не поддерживает индексирование.Если у кого-то есть предложения по этому вопросу, я весь слух.

Ответы [ 2 ]

0 голосов
/ 01 октября 2018

Оказывается, в .NET 4.7.2 ArraySegment<T> не предоставляет индексатор, если только он не приведен к интерфейсу IList<T>, а в .NET Core 2.1.

Youможет привести к интерфейсу IReadOnlyList<T>;обратите внимание, что это не мешает вам изменять сами содержащиеся объекты, если они изменчивы:

IReadOnlyList<T> представляет список, в котором количество и порядок элементов списка доступны только для чтения.Контент элементов списка не гарантированно доступен только для чтения.

Таким образом, он только гарантирует, что сама коллекция (контейнер) является неизменной (без добавления, удаления, замены).Вы не можете заменить элемент, потому что индексатор не имеет установщика.

Существует также класс оболочки ReadOnlyCollection<T> (добавьте using System.Collections.ObjectModel;).Также нет установщика для индексатора.

Если ни один из них не работает для вас, и вы хотите реализовать оболочку, просто создайте класс, который принимает в конструкторе IList<T>, и реализуйте ваш собственный индексатор .Я бы также реализовал IEnumerable<T>, чтобы вы получили поддержку LINQ, и она также заставит его работать с циклом foreach.

// Add these...
using System.Collections;
using System.Collections.Generic;

//...

public class MyWrapper<T> : IEnumerable<T>
{
  private IList<T> _internalList;

  public MyWrapper(IList<T> list)
  {
    _internalList = list;
  }

  public int Count => _internalList.Count;

  // the indexer
  public T this[int index]
  {
    get => _internalList[index];
    set => _internalList[index] = value;
  }

  public IEnumerator<T> GetEnumerator()
  {
    return _internalList.GetEnumerator();
  }

  IEnumerator IEnumerable.GetEnumerator() 
  {
    return this.GetEnumerator();
  }
}

А затем просто передайте сегмент массива в конструктор (без приведения).

PS Если это какой-то внутренний код, используемый только в нескольких местах, написание оболочки может или не может стоить хлопот;можно использовать IList<T>, если ясно, каково было намерение.

Кроме того, если вы не против работать с копией массива, который ограничен толькодиапазон, определенный сегментом массива, и если стоимость операции копирования не имеет значения, вы можете вызвать либо метод расширения ToArray, либо ToList для сегмента.Но я подозреваю, что вы хотите обновить исходный массив, и вам просто нужно «окно» в сегмент.

0 голосов
/ 01 октября 2018

Вы по-прежнему можете использовать ArraySegment, что предотвратит добавление / удаление элементов и доступ / обновление элементов по индексу через .Array свойство

var numbers = new[] { 1, 2, 3, 4 };
var segment = new ArraySegment<int>(numbers);

segment.Array[2] = 101;

// segment contains now { 1, 2, 101, 4}

Вы всегда можете создать методы расширения для улучшения читабельности

public static void UpdateAt<T>(this ArraySegment<T> segment, int index, T value)
{
    segment.Array[index] = value;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...