Какие операции над коллекцией / ее элементами делают недействительным перечислитель - PullRequest
0 голосов
/ 26 ноября 2018

Как указано в документации метода IEnumerable<T>.GetEnumerator , некоторые операции над коллекцией могут сделать недействительным перечислитель.Очевидно, что добавление или удаление элементов будет иметь эффект.Но что именно имеет значение для изменения коллекции?Перечисляет ли счетчик изменения в элементах самой коллекции?

Ответ на этот вопрос может быть скрыт в ответе на в этой теме , но я пропускаю некоторые примеры.

Пример кода:

public class CollectionElement
{
  public CollectionElement(int id, object someProperty)
  {
    ID = id;
    SomeProperty = someProperty;
  }

  public int ID { get; }
  public object SomeProperty { get; set; }
}

public class CollectionModifier
{
  // This is the example collection. (List<T> implements ICollection<T>.)
  private List<CollectionElement> collection = new List<CollectionElement>();

  // This is another example. (Dictionary<TKey, TValue> implements ICollection<KeyValuePair<TKey, TValue>>)
  private Dictionary<int, CollectionElement> dictionary = new Dictionary<int, CollectionElement>();

  private void Add(int id, object someProperty)
  {
    CollectionElement newElement = new CollectionElement(id, someProperty);

    // Both statements are obviously invalidating the enumerator of the corresponding collection.
    collection.Add(newElement);
    dictionary.Add(id, newElement);
  }

  private void Remove(int id)
  {
    // Both statements are obviously invalidating the enumerator of the corresponding collection.
    collection.RemoveAll(item => item.ID == id);
    dictionary.Remove(id);
  }

  private void ExchangeListElement(int index, CollectionElement newElement)
  {
    if (index >= collection.Count)
      return;
    // According to the comment by Dennis_E the following statement is invalidating the enumerator of the collection.
    collection[index] = newElement;
  }

  private void ModifyElement(int id, object newValue)
  {
    CollectionElement element = collection.FirstOrDefault(item => item.ID == id);
    if (element == null)
      return;
#warning Is the following statement modifying the collection, hence invalidating the enumerator?
    element.SomeProperty = newValue;
  }

  private void ExchangeElement(int id, CollectionElement newElement)
  {
    if (!dictionary.TryGetValue(id, out CollectionElement oldElement))
      return;
#warning Is the following statement modifying the collection, hence invalidating the enumerator?
    dictionary[id] = newElement;
  }
}

Ответы [ 2 ]

0 голосов
/ 26 ноября 2018

В дополнение к ответу Servy:

private int? _someValue;
public IEnumerable<int> GetValues()
{
   if(_someValue != null)
   {
       yield return _someValue.Value;
   }
}

Единственный, кто отвечает за правильность этого кода - это владелец кода.Если вы разрешите перечисление при изменении _someValue на null - вы получите исключение NullReferenceException в многопоточной среде.Еще хуже, если ваш объект имеет состояние:

private bool _isBlack = false;
private bool _isWhite = true;
public IEnumerable<string> GetValues()
{
   if(_isBlack)
   {
       yield return "black";
   }
   if(_isWhite)
   {
       yield return "white";
   }
}

В этом примере, если кто-то начал перечисление, пропустил слово "white", а затем включил черный - тот, кто перечисляет, не получит это "черное" значение.

0 голосов
/ 26 ноября 2018

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

Некоторые реализации используют самый простой вариант и говорят, что любое изменение в коллекции делает недействительным перечислитель (многие из коллекций .NET реализованы таким образом), другие могут продолжать итерациюпоследовательность независимо от каких-либо изменений в основной коллекции вообще.Некоторые находятся между ними.

Если вы хотите знать, как поведет себя данная последовательность, если вы измените коллекцию, на которой она основана, вам нужно будет просмотреть документацию для этой коллекции или все, что сгенерировалопоследовательность для этого.Если вы создаете свою собственную коллекцию и / или итератор для коллекции, то вы сами решаете, какие типы изменений больше не имеют смысла, чтобы какие-либо существующие итераторы продолжали обработку или стоило ли это вашего времени (и связанного с этимзатраты на производительность) для поддержки некоторых / всех изменений в базовой коллекции, сохраняя при этом разумное поведение для последовательностей, которые повторяются.

...