Поведение перехвата в цикле foreach C#

Думаю, простой вопрос --> Насколько я понимаю, воображаемый объект IEnumerator используется, когда я использую цикл foreach для объекта IEnumerable. Мой вопрос заключается в следующем:

Как я могу «поймать» недопустимое поведение в циклах foreach, связанных с моими объектами? Я специально хочу увидеть, был ли изменен мой исходный объект, из которого был создан IEnumerable. Если оригинал был изменен, я хочу создать исключение.

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


person BlackVegetable    schedule 14.09.2011    source источник
comment
воображаемый объект IEnumerator Это вовсе не воображаемый объект. Звонок на IEnumerable.GetEnumerator() действительно есть.   -  person BoltClock    schedule 14.09.2011
comment
Проверьте stackoverflow.com/questions/7396112/ Ваше здоровье!   -  person Annie Lagang    schedule 14.09.2011
comment
@BoltClock: если вы делаете foreach над массивом или строкой, IEnumerator действительно является воображаемым - вместо этого компилятор создает цикл for.   -  person Gabe    schedule 14.09.2011
comment
@Eric: Я предполагаю, что OP использует термин «воображаемый» для обозначения того, что объект недоступен из вашего кода. Я бы, наверное, назвал это анонимным.   -  person Gabe    schedule 14.09.2011
comment
Я не понимаю вопрос. можно поконкретнее и точнее?   -  person Artur Mustafin    schedule 14.09.2011
comment
-1 В чем вопрос?   -  person Artur Mustafin    schedule 14.09.2011


Ответы (2)


Интерфейс 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.
}
person Bernie White    schedule 14.09.2011
comment
Если он проверяет только изменился ли элемент, _Items.Any(x =› x.HasChanged) будет, вероятно, понятнее. - person dlev; 14.09.2011

Стоит отметить, что цикл foreach в vb.net или c# будет выполнять утиную типизацию метода GetEnumerator, возвращающего объект, с методом MoveNext, возвращающим логическое значение, и свойством Current, возвращающим подходящий тип. Хотя метод GetEnumerator() обычно является реализацией IEnumerable‹T›.GetEnumerator(), это не обязательно. В некоторых случаях производительность может быть выше, если GetEnumerator() возвращает тип значения, а не тип класса или упакованный IEnumerable‹T›. Таким образом, возможно, что вызов цикла foreach для объекта, который реализует IEnumerable‹T›, будет использовать другую семантику, чем если бы объект сначала был приведен к IEnumerable‹T›.

person supercat    schedule 14.09.2011