集合/元素上的哪些操作使枚举器无效

时间:2018-11-26 18:06:03

标签: c# collections enumerator

IEnumerable<T>.GetEnumerator method文档中所述 集合上的某些操作可能会使枚举数无效。很明显,添加或删除元素将具有上述效果。但是什么才算修改集合呢?枚举器是否关心集合本身元素的更改?

这个问题的答案可能隐藏在this thread的答案中,但我缺少一些示例。

示例代码:

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 :(得分:1)

这完全取决于实现。甚至添加或删除元素都需要使枚举器无效,这甚至不是事实。取决于迭代器的实现(以及潜在的基础集合的详细信息),以确定对基础集合的哪些类型的更改使其不再可能继续枚举,以及哪些更改仍可以允许迭代器继续。

某些实现选择最简单的选项,并说对集合的任何更改会使枚举器无效(许多.NET集合以这种方式实现),而其他实现则可以继续迭代序列,无论基础集合的所有更改。有些介于两者之间。

如果您想知道给定序列在更改基于其的集合时将如何表现,则必须查看该集合的文档或生成该序列的任何内容。如果您要为集合创建自己的集合和/或迭代器,则由您决定哪种类型的更改使任何现有的迭代器继续处理不再明智,或者是否值得您花时间(以及相关的时间)性能成本),以支持对基础集合的部分/全部更改,同时仍对迭代的序列保持明智的行为。

答案 1 :(得分:-1)

除了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”,则有人启用了黑色-枚举的人将不会获得此“ black”值。