Why Does Ienumerator<T> Inherit from Idisposable While the Non-Generic Ienumerator Does Not

Why does IEnumeratorT inherit from IDisposable while the non-generic IEnumerator does not?

Basically it was an oversight. In C# 1.0, foreach never called Dispose 1. With C# 1.2 (introduced in VS2003 - there's no 1.1, bizarrely) foreach began to check in the finally block whether or not the iterator implemented IDisposable - they had to do it that way, because retrospectively making IEnumerator extend IDisposable would have broken everyone's implementation of IEnumerator. If they'd worked out that it's useful for foreach to dispose of iterators in the first place, I'm sure IEnumerator would have extended IDisposable.

When C# 2.0 and .NET 2.0 came out, however, they had a fresh opportunity - new interface, new inheritance. It makes much more sense to have the interface extend IDisposable so that you don't need an execution-time check in the finally block, and now the compiler knows that if the iterator is an IEnumerator<T> it can emit an unconditional call to Dispose.

EDIT: It's incredibly useful for Dispose to be called at the end of iteration (however it ends). It means the iterator can hold on to resources - which makes it feasible for it to, say, read a file line by line. Iterator blocks generate Dispose implementations which make sure that any finally blocks relevant to the "current point of execution" of the iterator are executed when it's disposed - so you can write normal code within the iterator and clean-up should happen appropriately.


1 Looking back at the 1.0 spec, it was already specified. I haven't yet been able to verify this earlier statement that the 1.0 implementation didn't call Dispose.

Implementing IEnumerator/ IEnumerable with IDispose error

IEnumerator<T> inherits from IDisposable, you so have to implement it too and provide a Dispose method.

IEnumerator<T> inherits from IEnumerator, so you have to implement it too and provide a GetEnumerator() function returning an IEnumerator.

IEnumerator<T> defines a property Current which returns a T, so you have to implement one.


I guess the confusion arises from the fact that the generic IEnumerable<T> and IEnumerator<T> inherit from the non-generic versions (IEnumerable and IEnumerator), demanding to implement methods with the same name but different return value types; and that IEnumerator<T> inherits from IDisposable while IEnumerator does not.


So, in the end, your class should look something like

public class AppAnnieImport : IEnumerator<AppLines>, IEnumerable<AppLines>
{
public int code { get; set; }
public DateTime end_date { get; set; }
public string vertical { get; set; }
public string granularity { get; set; }
public string device { get; set; }
public List<AppLines> AppLine { get; set; }
private int position;

//IEnumerator and IEnumerable require these methods.
public IEnumerator<AppLines> GetEnumerator()
{
return (IEnumerator<AppLines>)this;
}

IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator<AppLines>)this;
}

//IEnumerator
public bool MoveNext()
{
position++;
return (position < AppLine.Count);
}

//IEnumerable
public void Reset()
{ position = 0; }

//IEnumerable
object IEnumerator.Current
{
get { return (AppLine.ToArray())[position] ; }
}

public AppLines Current
{
get { return (AppLine.ToArray())[position] ; }
}

public void Dispose()
{
// do something or not
}
}

making use of explicit interface implementation (you may also want to get rid of the code duplication).

IEnumerator: Is it normal to have an empty Dispose method?

Yes, it is.

IEnumerator<T> implements IDisposable in case you make an enumerator that does need to be disposed. Since most enumerators don't need to be disposed, the method will usually be empty.

By the way, you can implement your IEnumerator more easily by creating an iterator.

Why does the using statement work on IEnumerator and what does it do?

Please, check the following link about foreach statement. It uses try/finally block with Dispose call if it's possible. That's the code which is behind using statement.

Does the non-generic version of IEnumerable support deferred execution?

Yes, it is supported ever since the yield keyword was. The only difference is that it's more or less IEnumerable<object>, which might lead to inefficiencies if it has to do boxing. Other than that, it's exactly the same.

Why generic ICollectionT does not inherit some non-generic interface with Count property?

Is it just bad design choice?

Probably the answer is yes.

And to solve this issue in .NET 4.5 MS introduced IReadOnlyCollection<out T> interface, which is covariant for reference types.

So you can rewrite your code like following

static int? FastCountOrZero(this IEnumerable items)
{
if (items == null)
return 0;
var collection = items as ICollection;
if (collection != null)
return collection.Count;
var roCollection = items as IReadOnlyCollection<object>; // only for reference types
if (roCollection != null)
return roCollection.Count;
var source = items as IQueryable;
if (source != null)
return QueryableEx.Count(source);

return items.Cast<object>().Count();
}

And as last resort you can cast items to dynamic object and invoke Count property dynamicly.

if (items.GetType().GetInterface("System.Collections.Generic.ICollection`1") != null)
{
dynamic dynamic = items;
return dynamic.Count;
}


Related Topics



Leave a reply



Submit