Is it possible to write multiple iterators for a type in C #?
So for a type like:
CoolCollection<T>
you may have:
foreach (T item in coolCollection)
{
...
}
foreach (CoolNode node in coolCollection)
{
...
}
If this is not possible, perhaps like foreach2, or some other way of iterating. Often I would like more than one way to repeat on a type.
EDIT: Sorry if this was not clear. Basically CoolNode is the node that CoolCollection is doing. CoolNode has a property called value to return T, but I need another iterator to only return CoolNodes.
EDIT2: I can't do a coolCollection. Something is iterating because the CoolNodes are linked through a property called Next like LinkedList. So I need to implement 2 iterators.
a source to share
Just make it CoolCollection<T>
explicitly implementable IEnumerable<CoolNode<T>>
as well IEnumerable<T>
. (I assume this is valid CoolNode<T>
, but if not, just take the extra <T>
everywhere).
This will allow you to iterate in both directions, although you need a cast.
To do this, you need something like:
class CoolCollection<T> : ICollection<T>, IEnumerable<CoolNode<T>>
{
IEnumerator<CoolNode<T>> IEnumerable<CoolNode<T>>.GetEnumerator()
{
///...Do work here...
}
IEnumerator<T> GetEnumerator()
{
///...Do work here...
}
}
Using it would be like this:
foreach (T item in coolCollection)
{
...
}
foreach (CoolNode<T> node in (IEnumerable<CoolNode<T>>)coolCollection)
{
...
}
Another option is to expose a property for "nodes" so you can:
foreach(var nodes in coolCollection.Nodes)
{ ... }
To make this possible, you will change the situation a little. You would need to make a private class that implemented the enumerator ... something like:
class CoolCollection<T> : ICollection<T>
{
private List<CoolNode<T>> nodes;
IEnumerable<CoolNode<T>> Nodes
{
get
{
foreach(var node in this.nodes) { yield return node; }
}
}
}
a source to share
No, you cannot do this. You cannot overload the default iterator.
Imagine if you can overload the default iterator.
What would it do? foreach (object o in foo)
, there will be no logical choice of the correct iterator.
What you can do is have a second method named ForEach2 that iterates through your collection in a different way. Or you can explicitly implement the interface. Or you can use Linq composition for this kind of thing.
In terms of class design:
interface IBar {
IEnumerator<string> GetEnumerator();
}
class Foo : IBar, IEnumerable<int> {
// Very bad, risky code. Enumerator implementations, should
// line up in your class design.
public IEnumerator<int> GetEnumerator()
{
yield return 1;
yield return 2;
yield return 3;
yield return 4;
}
IEnumerator<string> IBar.GetEnumerator()
{
yield return "hello";
}
// must be IEnumerable if you want to support foreach
public IEnumerable<string> AnotherIterator
{
get {
yield return "hello2";
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
LINQ Extensions for EveryPair
struct Pair<T> {
public T First;
public T Second;
}
static class LinqExtension {
public static IEnumerable<Pair<T>> EachPair<T>(this IEnumerable<T> input) {
T first = default(T);
bool gotFirst = false;
foreach (var item in input)
{
if (!gotFirst)
{
first = item;
gotFirst = true;
}
else {
yield return new Pair<T>() { First = first, Second = item };
gotFirst = false;
}
}
}
}
Test code:
class Program
{
static void Main(string[] args)
{
var foo = new Foo();
foreach (int number in foo)
{
Console.WriteLine(number);
}
// LINQ composition - a good trick where you want
// another way to iterate through the same data
foreach (var pair in foo.EachPair())
{
Console.WriteLine("got pair {0} {1}", pair.First, pair.Second);
}
// This is a bad and dangerous practice.
// All default enumerators should be the same, otherwise
// people will get confused.
foreach (string str in (IBar)foo)
{
Console.WriteLine(str);
}
// Another possible way, which can be used to iterate through
// a portion of your collection eg. Dictionary.Keys
foreach (string str in foo.AnotherIterator)
{
Console.WriteLine(str);
}
}
a source to share
If CoolCollection implements IEnumerable, you can write:
foreach (var item in coolCollection)
{
...
}
or if T is CoolNode
foreach (CoolNode node in coolCollection)
{
...
}
If you need to somehow convert each item to its own type, you can use the Linq Select statement:
foreach (CoolNode node in coolCollection.Select(item => ConvertToNode(item))
{
...
}
a source to share
Take a look at the snippet iterindex
. In your class, enter iterindex
and click [TAB]
. This will help you implement the Named Iterator pattern.
The result can be used like this:
foreach (var e in myTree.DepthFirstView) // supports iteration
{
if (e == myTree.DepthFirstView[2]) // supports indexing
{
// ...
}
}
(I wrote this snippet, but I suspect it was never actually used to be used.)
a source to share