tags:

views:

308

answers:

5

So for a type like:

CoolCollection<T>

you could have:

foreach (T item in coolCollection)
{
    ...
}

foreach (CoolNode node in coolCollection)
{
    ...
}

If this isn't possible, maybe like foreach2, or some other way to iterate. Often times, I would really like more than 1 way of iterating on a type.

EDIT: Sorry if it wasn't clear. Basically CoolNode is a node that makes CoolCollection. CoolNode has a property called value to return T, but I need another iterator to return only CoolNodes.

EDIT2: I can't do coolCollection.Something to iterate, because CoolNodes are connected via a property called Next, like a LinkedList. So I need to implement 2 iterators.

+1  A: 

No, you can't do that. You can not overload your default iterator.

Imagine if you could overload your default iterator.

What would this do? foreach (object o in foo) , there would be no logical way to choose the right iterator.

What you can do is have a second method named ForEach2 that iterates through your collection in a different way. Or you could explicitly implement an interface. Or you could use Linq composition for this kind of stuff.

From a class design perspective:

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 EachPair

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);
        }
    }
Sam Saffron
Can someone tell me why this is the worse answer?
Sam Saffron
In first block: IEnumerable<string> AnotherIterator should be IEnumerator<string>
Reed Copsey
Reed Copsey
@Reed, in the question @Joan mentions 'maybe like foreach2' so I demonstrated a generic way of implementing foreach2
Sam Saffron
@sambo99: I understood "foreach2" to be a second foreach, not something that iterated in pairs. Your answer makes sense now, but I'm not sure that's what she meant. ;)
Reed Copsey
@Reed, from FDG Do not use IEnumerator<T> ... except as the return type of a GetEnumerator method ...
Sam Saffron
It has to be IEnumerable or it will not compile ....
Sam Saffron
Thanks sambo, I added +1 for the detailed answer.
Joan Venge
+3  A: 

Just make CoolCollection<T> explicitly implement IEnumerable<CoolNode<T>> as well as IEnumerable<T>. (I'm guessing it's really CoolNode<T>, but if not, just take the extra <T> out everywhere.)

This will let you iterate in both manners, although you'll need a cast.

To do this, you'd 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 this would be like so:

foreach (T item in coolCollection)
{
    ...
}


foreach (CoolNode<T> node in (IEnumerable<CoolNode<T>>)coolCollection)
{
    ...
}

The other option would be to expose a property for the "nodes", so you could do:

foreach(var nodes in coolCollection.Nodes)
{ ... }

To implement this, you'd change things around a little bit. You'd 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; }
        }
    }
}
Reed Copsey
Thanks Reed. Why will I need a cast? Also I edited the question to make it clear, if it helps.
Joan Venge
I added an example - you'd need the cast so the compiler treats coolCollection as IEnumerable<CoolNode> instead of IEnumerable<T> in that case. I'm also guessing it's CoolNode<T>, if the nodes are part of the generic collection...
Reed Copsey
At least you should mention that this is a bad practice ...
Sam Saffron
@sambo99: Good point. I'd personally do my second option, if I were implementing this. I'd make my collection implement IEnumerable<T>, and have a property that implemented IEnumerable<CoolNode<T>> to return the nodes. I think it's much cleaner and easier to follow.
Reed Copsey
@Reed, do I get a +1 one for that :P, Im frustrated that my long detailed answer is still sitting at -1
Sam Saffron
@sambo99: Sure - I'm going to comment on yours in a second, though ;)
Reed Copsey
Thanks Reed. This looks like what I need. Now that I thought about the property method (your #2 solution makes sense). Can you please give me an example of how you would implement that? I am not sure how I could implement an iterator in a property.
Joan Venge
@Joan Venge: It's there now... You need to implement an IEnumerable private class for this, but it's a fairly easy one to implement...
Reed Copsey
Thanks Reed. Also why did you create a constructor for the NodeEnumerator? To make it generic? Do you think I should just make it specific to the CoolCollection? In terms of good practice I mean.
Joan Venge
@Reed @Joan, I see no reason to go about creating the NodeEnumerator private class (unless its for a reuse scenario in which case it would not be a private class), you can yield return directly from you Nodes IEnumerable.
Sam Saffron
@sambo99: Good point. I updated to simplify.
Reed Copsey
@Joan: I updated it to remove the need for the private class.. it simplifies things a fair amount. Sambo99 had a good point... The option above is a good, clean way of implementing this (now).
Reed Copsey
Thanks guys....
Joan Venge
+1  A: 

If I understand the question correctly...

You could do it similar to the some of the other collection objects do it:

for example:

foreach (int key in IDictionary.Keys)
{

}

foreach (object value in IDictionary.Values)
{

}

But I don't think there is a way to do exactly the way you have it written...

J.13.L
A: 

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 somehow transform each item to your type, you can use Linq Select operator:

foreach (CoolNode node in coolCollection.Select(item => ConvertToNode(item))
{
    ...
}
bbmud
A: 

Take a look at the iterindex snippet. In your class, type iterindex and hit [TAB]. It will help you implement a "Named Iterator and Indexer" pattern.

The result can by 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 has never been put to good use. Ever.)

Jay Bazuzi