Reputation: 13064
Simple question, I believe --> It is my understanding that an imaginary IEnumerator object is being used when I use a foreach loop over an IEnumerable object. My question is as follows:
How can I "catch" illegal behavior in foreach loops dealing with my objects? I specifically want to see if my original object from which the IEnumerable was created has been modified. If the original has been modified, I want to throw an exception.
My current approach has been to use a version number. This works great if I create an explicit iterator and use a MoveNext() or somesuch, but foreach loops seem to trick my version-based approach.
Upvotes: 0
Views: 264
Reputation: 5105
The IEnumerable interface is what actualy returns the IEnumerator it is the whole reason this interface exists.
When the foreach block is call the IEnumerable.GetEnumerator() method is called to get the enumerator.
If you want to track each item then because they are object types they will be referenced so you will have the update to date version. Within the item you could implement the versioning that you mentioned like so.
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;
}
}
Elsewhere you could use something like this in your loop.
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()
}
}
UPDATED
Alternatively if you just want to find out what items have changed you could do something like this.
SomeItem[] changedItems = _Items.Where(item => item.HasChanged);
As per dlev's suggestion if you just want to know if any changed just use.
if (_Items.Any(item => item.HasChanged))
{
// An item has changed, do stuff.
}
Upvotes: 1
Reputation: 81237
It's worthwhile to note that a foreach loop in vb.net or c# will duck-type a GetEnumerator method that returns an object with a MoveNext method that returns Boolean and a Current property that returns a suitable type. Although the GetEnumerator() method will usually be an implementation of IEnumerable<T>.GetEnumerator(), it doesn't have to be. In some cases, performance may be better if GetEnumerator() returns a value type rather than a class-type or boxed IEnumerable<T>. It's thus possible that invoking a foreach loop on an object which implements IEnumerable<T> will use different semantics than if the object were cast to IEnumerable<T> first,.
Upvotes: 2