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.

+1


a source to share


5 answers


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; }
        }
    }
}

      

+3


a source


If I understood the question correctly ...

You can do it in the same way as other collection objects do:

eg:



foreach (int key in IDictionary.Keys)
{

}

foreach (object value in IDictionary.Values)
{

}

      

But I don't think there is a way to do exactly how you wrote it ...

+2


a source


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);
        }
    }

      

+1


a source


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))
{
    ...
}

      

0


a source


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.)

0


a source







All Articles