0

简单的问题,我相信 --> 我的理解是,当我在 IEnumerable 对象上使用 foreach 循环时,正在使用一个虚构的 IEnumerator 对象。我的问题如下:

如何在处理我的对象的 foreach 循环中“捕捉”非法行为?我特别想查看创建 IEnumerable 的原始对象是否已被修改。如果原来的已经被修改了,我想抛出一个异常。

我目前的方法是使用版本号。如果我创建一个显式迭代器并使用 MoveNext() 或类似的方法,这非常有用,但是 foreach 循环似乎欺骗了我的基于版本的方法。

4

2 回答 2

2

值得注意的是,vb.net 或 c# 中的 foreach 循环将使用返回 Boolean 的 MoveNext 方法和返回合适类型的 Current 属性返回对象的 GetEnumerator 方法。虽然 GetEnumerator() 方法通常是 IEnumerable<T>.GetEnumerator() 的实现,但它不一定是。在某些情况下,如果 GetEnumerator() 返回值类型而不是类类型或装箱的 IEnumerable<T>,性能可能会更好。因此,在实现 IEnumerable<T> 的对象上调用 foreach 循环可能会使用与首先将对象强制转换为 IEnumerable<T> 时不同的语义。

于 2011-09-14T04:08:08.043 回答
1

IEnumerable 接口实际上返回了 IEnumerator,这是该接口存在的全部原因。

当调用 foreach 块时,调用 IEnumerable.GetEnumerator() 方法来获取枚举器。

如果您想跟踪每个项目,那么因为它们是对象类型,它们将被引用,因此您将获得最新版本的更新。在项目中,您可以像这样实现您提到的版本控制。

public class SomeItem
{
  private string _Value;
  private int _Version = 0;
  private int _LastKnown = 0;

  public SomeItem()
  {

  }

  public SomeItem(string value)
  {
     this._Value = value
  }


  public string Value
  {
     get { return this._Value; }
     set { if (this._Value != value) { this._Value = value; this._Version++; } }
  }

  public int Version
  {
    get { return this._Version; }
  }

  public bool HasChanged
  {
     get { return (this._Version != _LastKnown); }
  }

 // Reset change tracking.
 public void Reset()
 {
    this._LastKnown = this._Version;
 }
}

在其他地方,您可以在循环中使用类似的东西。

private List<SomeItem> _Items = new List<SomeItem>();

// Add an item.
_Items.Add(new SomeItem("ABC"));

// Add a changed item.
SomeItem itemN = new SomeItem("EFG");
itemN.Value = "XYZ";
_Items.Add(itemN);


foreach (SomeItem item in _Items)
{
   if (item.HasChanged)
   {
      // Do stuff here.
      //throw new Exception()
   }
}

更新

或者,如果您只是想找出哪些项目发生了变化,您可以这样做。

SomeItem[] changedItems = _Items.Where(item => item.HasChanged);

根据 dlev 的建议,如果您只想知道是否有任何更改,请使用。

if (_Items.Any(item => item.HasChanged))
{
   // An item has changed, do stuff.
}
于 2011-09-14T03:18:48.247 回答