Reputation: 28079
I know how to implement the non generic IEnumerable, like this:
using System;
using System.Collections;
namespace ConsoleApplication33
{
class Program
{
static void Main(string[] args)
{
MyObjects myObjects = new MyObjects();
myObjects[0] = new MyObject() { Foo = "Hello", Bar = 1 };
myObjects[1] = new MyObject() { Foo = "World", Bar = 2 };
foreach (MyObject x in myObjects)
{
Console.WriteLine(x.Foo);
Console.WriteLine(x.Bar);
}
Console.ReadLine();
}
}
class MyObject
{
public string Foo { get; set; }
public int Bar { get; set; }
}
class MyObjects : IEnumerable
{
ArrayList mylist = new ArrayList();
public MyObject this[int index]
{
get { return (MyObject)mylist[index]; }
set { mylist.Insert(index, value); }
}
IEnumerator IEnumerable.GetEnumerator()
{
return mylist.GetEnumerator();
}
}
}
However I also notice that IEnumerable has a generic version, IEnumerable<T>
, but I can't figure out how to implement it.
If I add using System.Collections.Generic;
to my using directives, and then change:
class MyObjects : IEnumerable
to:
class MyObjects : IEnumerable<MyObject>
And then right click on IEnumerable<MyObject>
and select Implement Interface => Implement Interface
, Visual Studio helpfully adds the following block of code:
IEnumerator<MyObject> IEnumerable<MyObject>.GetEnumerator()
{
throw new NotImplementedException();
}
Returning the non generic IEnumerable object from the GetEnumerator();
method doesn't work this time, so what do I put here? The CLI now ignores the non generic implementation and heads straight for the generic version when it tries to enumerate through my array during the foreach loop.
Upvotes: 175
Views: 190128
Reputation: 899
.... and do not forget the using if you are blocked by
IEnumerator IEnumerable.GetEnumerator()
{
// uses the strongly typed IEnumerable<T> implementation
return GetEnumerator();
}
add this:
using System.Collections;
also:
: IEnumerable<string>
is sufficient has it is embedding the IEnumerable:
public interface IEnumerable<out T> : IEnumerable
... also beware of Syntax jokes between the A in enumrAble and the o in enumratOr which are both used in this pattern ! Nice dangerous line:
IEnumerator IEnumerable.GetEnumerator()
Upvotes: 0
Reputation: 64138
You probably do not want an explicit implementation of IEnumerable<T>
(which is what you've shown).
The usual pattern is to use IEnumerable<T>
's GetEnumerator
in the explicit implementation of IEnumerable
:
class FooCollection : IEnumerable<Foo>, IEnumerable
{
SomeCollection<Foo> foos;
// Explicit for IEnumerable because weakly typed collections are Bad
System.Collections.IEnumerator IEnumerable.GetEnumerator()
{
// uses the strongly typed IEnumerable<T> implementation
return this.GetEnumerator();
}
// Normal implementation for IEnumerable<T>
IEnumerator<Foo> GetEnumerator()
{
foreach (Foo foo in this.foos)
{
yield return foo;
//nb: if SomeCollection is not strongly-typed use a cast:
// yield return (Foo)foo;
// Or better yet, switch to an internal collection which is
// strongly-typed. Such as List<T> or T[], your choice.
}
// or, as pointed out: return this.foos.GetEnumerator();
}
}
Upvotes: 95
Reputation: 8762
Note that the IEnumerable<T>
allready implemented by the System.Collections
so another approach is to derive your MyObjects
class from System.Collections
as a base class (documentation):
System.Collections: Provides the base class for a generic collection.
We can later make our own implemenation to override the virtual System.Collections
methods to provide custom behavior (only for ClearItems
, InsertItem
, RemoveItem
, and SetItem
along with Equals
, GetHashCode
, and ToString
from Object
). Unlike the List<T>
which is not designed to be easily extensible.
Example:
public class FooCollection : System.Collections<Foo>
{
//...
protected override void InsertItem(int index, Foo newItem)
{
base.InsertItem(index, newItem);
Console.Write("An item was successfully inserted to MyCollection!");
}
}
public static void Main()
{
FooCollection fooCollection = new FooCollection();
fooCollection.Add(new Foo()); //OUTPUT: An item was successfully inserted to FooCollection!
}
Please note that driving from collection
recommended only in case when custom collection behavior is needed, which is rarely happens. see usage.
Upvotes: 0
Reputation: 5042
If you choose to use a generic collection, such as List<MyObject>
instead of ArrayList
, you'll find that the List<MyObject>
will provide both generic and non-generic enumerators that you can use.
using System.Collections;
class MyObjects : IEnumerable<MyObject>
{
List<MyObject> mylist = new List<MyObject>();
public MyObject this[int index]
{
get { return mylist[index]; }
set { mylist.Insert(index, value); }
}
public IEnumerator<MyObject> GetEnumerator()
{
return mylist.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Upvotes: 196
Reputation: 69280
Why do you do it manually? yield return
automates the entire process of handling iterators. (I also wrote about it on my blog, including a look at the compiler generated code).
If you really want to do it yourself, you have to return a generic enumerator too. You won't be able to use an ArrayList
any more since that's non-generic. Change it to a List<MyObject>
instead. That of course assumes that you only have objects of type MyObject
(or derived types) in your collection.
Upvotes: 26
Reputation: 13296
If you work with generics, use List instead of ArrayList. The List has exactly the GetEnumerator method you need.
List<MyObject> myList = new List<MyObject>();
Upvotes: 5