31

I am writing a custom ConfigurationElementCollection for a custom ConfigurationHandler in C#.NET 3.5 and I am wanting to expose the IEnumerator as a generic IEnumerator.

What would be the best way to achieve this?

I am currently using the code:

public new IEnumerator<GenericObject> GetEnumerator()
{
  var list = new List();
  var baseEnum = base.GetEnumerator();
  while(baseEnum.MoveNext())
  {
    var obj = baseEnum.Current as GenericObject;
    if (obj != null)
      list.Add(obj);
  }
  return list.GetEnumerator();
}

Cheers

zonkflut
  • 2,929
  • 3
  • 22
  • 25

5 Answers5

58

I don't believe there's anything in the framework, but you could easily write one:

IEnumerator<T> Cast<T>(IEnumerator iterator)
{
    while (iterator.MoveNext())
    {
        yield return (T) iterator.Current;
    }
}

It's tempting to just call Enumerable.Cast<T> from LINQ and then call GetEnumerator() on the result - but if your class already implements IEnumerable<T> and T is a value type, that acts as a no-op, so the GetEnumerator() call recurses and throws a StackOverflowException. It's safe to use return foo.Cast<T>.GetEnumerator(); when foo is definitely a different object (which doesn't delegate back to this one) but otherwise, you're probably best off using the code above.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 7
    Can't you just return the Enumerator from cast? return this.Cast().GetEnumerator(); – flq May 06 '09 at 12:42
  • 2
    @flq, Jon: I am doing the `this.Cast().GetEnumerator()` solution, in the same scenario (though I am deriving from `IEnumerable`), and I am getting a stack overflow. – Merlyn Morgan-Graham Mar 30 '11 at 09:26
  • @Merlyn: I suggest you post a short but complete example as another Stack Overflow question. – Jon Skeet Mar 30 '11 at 10:02
  • @Jon: Your original yield return suggestion solves my problem, as does using OfType instead. But I can post a question anyway. – Merlyn Morgan-Graham Mar 30 '11 at 10:11
  • @Merlyn: If you're interested in what's going wrong, then go ahead - I can't quite imagine what you're doing at the moment, so it's hard to say really. – Jon Skeet Mar 30 '11 at 10:18
  • 1
    Use OfType() to avoid the stack overflow exception, see bug report http://connect.microsoft.com/VisualStudio/feedback/details/713688/stackoverflowexception-when-calling-enumerable-cast-t-on-domaincollectionview-titem – jorgebg Aug 28 '12 at 08:54
  • @MerlynMorgan-Graham: we had exactly the same problem with a Stack Overflow when trying to implement IEnumerator in a class derived from ConfigurationElementCollection and implementing ICollection – Jedidja Apr 18 '13 at 14:03
  • I was running into the same Stack Overflow problem. In my case it was due to the fact that the GetEnumerator needed to be to the base.GetEnumerator otherwise you loop within your own GetEnumerator redefinition. – VeV Jun 10 '15 at 23:03
  • @VeV: Without seeing the exact code, it's hard to know exactly what's going on there - as per my response to Merlyn, it would be good to ask a new question. – Jon Skeet Jun 11 '15 at 06:11
  • @HaraldDutch: Please don't perform such significant edits to answers; I'd be happy to edit the answer when it's actually clear what's wrong, but Merlyn Morgan-Graham's comment about a stack overflow didn't actually provide an example. Using `OfType` will not do the same as `Cast`. I'll edit to clarify the difference between them. – Jon Skeet Jun 23 '16 at 08:30
  • It throws a `StackOverflowException` if the class implements `IEnumerable` because Cast returns `source as IEnumerable` if it's not null. http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,27bb217a6d5457ec – borigas Sep 19 '16 at 20:22
  • @borigas: How would that mean it throws a StackOverflowException? That doesn't recurse... (I still haven't reproduced this StackOverflowException...) – Jon Skeet Sep 19 '16 at 20:43
  • `Cast` isn't where the `StackOverflowException` occurs, but when it returns `source as IEnumerable` without doing anything, it's effectively a noop, turning it into `public IEnumerator GetEnumerator() { return this.GetEnumerator(); }` See https://dotnetfiddle.net/Vh1USx for an example – borigas Sep 20 '16 at 20:24
  • @borigas: Ah, I see. But your fiddle leaves out the `new` part, which would prevent recursion, I believe - by delegating to the `IEnumerable.GetEnumerator()` call. But there's definitely room for doubt - I'll see if I can provoke it using *exactly* my code. – Jon Skeet Sep 20 '16 at 20:26
  • @borigas: And now I've found out how to... will edit. – Jon Skeet Sep 20 '16 at 20:31
3

IEnumerable<T> already derives from IEnumerable so there's no need to do any conversion. You can simply cast to it...well actually it's implicit no cast necessary.

IEnumerable<T> enumerable = GetGenericFromSomewhere();
IEnumerable sadOldEnumerable = enumerable;
return sadOldEnumerable.GetEnumerator();

Going the other way round isn't much more difficult with LINQ:

var fancyEnumerable = list.OfType<GenericObject>();
return fancyEnumerable.GetEnumerator();
Paul Alexander
  • 31,970
  • 14
  • 96
  • 151
  • My bad missed the generic part to my method definition – zonkflut May 06 '09 at 07:02
  • 2
    You are confusing IEnumerable and IEnumerator. IEnumerable derives from IEnumerable and IEnumerator derives from IEnumerator. It doesn't make much sense to say "IEnumerable theEnumerator" because an enumerable is not an enumerator. – Matthew Olenik May 06 '09 at 07:03
  • casting does not work as the in the parent object the collection is stored in an ArrayList. – zonkflut May 06 '09 at 07:05
  • Yep - read it too quickly. But the answer still applies. I'll fix it to reflect the correct classes. – Paul Alexander May 06 '09 at 07:05
  • It does not work for the case: var genericEnumerator = (IEnumerator)new ArrayList { 1 }.GetEnumerator(); throws exception: System.InvalidCastException: Unable to cast object of type 'ArrayListEnumeratorSimple' to type 'System.Collections.Generic.IEnumerator`1[System.Int32]'. – zonkflut May 06 '09 at 07:11
  • You can't cast from non-generic to generic. You have to use the OfType method mentioned in the second portion. You can only cast from generic to non-generic. – Paul Alexander May 06 '09 at 07:16
1

I was running into the same Stack Overflow problem mentioned is some of the comments. In my case it was due to the fact that the GetEnumerator call needed to be to the base.GetEnumerator otherwise you loop within your own GetEnumerator redefinition.

This is the code that was Throwing the Stack Overflow. The use of the foreach statement call the same GetEnumerator function I'm trying to overload:

public new IEnumerator<T> GetEnumerator()
{
    foreach (T type in this)
    {
        yield return type;
    }
}

I've ended up with a simplified version of the original post as you don't need to use a List holder.

public class ElementCollection<T> : ConfigurationElementCollection, IList<T>
    ...
    public new IEnumerator<T> GetEnumerator()
    {
        var baseEnum = base.GetEnumerator();
        while (baseEnum.MoveNext())
        {
            yield return baseEnum.Current as T;
        }
    }
    ...
}
VeV
  • 1,236
  • 14
  • 20
1

You can use OfType<T> and Cast<T>.

public static IEnumerable Digits()
{
    return new[]{1, 15, 68, 1235, 12390, 1239};
}

var enumerable = Digits().OfType<int>();
foreach (var item in enumerable)
    // var is here an int. Without the OfType<int(), it would be an object
    Console.WriteLine(i);

To get an IEnumerator<T> instead of an IEnumerable<T> you can just make a call to GetEnumerator()

var enumerator = Digits().OfType<int>().GetEnumerator();
Svish
  • 152,914
  • 173
  • 462
  • 620
0

This works for me.

IEnumerator<DataColumn> columnEnumerator = dt.Columns.Cast<DataColumn>().GetEnumerator();