Поймать поведение в цикле foreach C # - PullRequest
0 голосов
/ 14 сентября 2011

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

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

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

Ответы [ 2 ]

2 голосов
/ 14 сентября 2011

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

1 голос
/ 14 сентября 2011

Интерфейс 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.
}
...