是否可以为C#中的类型编写多个迭代器?

时间:2009-05-14 22:18:37

标签: c# .net iterator

对于类似的类型:

CoolCollection<T>
你可以:

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

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

如果这不可能,可能像foreach2,或其他一些迭代方式。很多时候,我真的想要一种迭代类型的方法。

编辑:对不起,如果不清楚的话。基本上CoolNode是一个生成CoolCollection的节点。 CoolNode有一个名为value的属性来返回T,但是我需要另一个迭代器才能返回CoolNodes。

EDIT2:我不能做coolCollection.Something迭代,因为CoolNodes通过一个名为Next的属性连接,就像LinkedList一样。所以我需要实现2个迭代器。

5 个答案:

答案 0 :(得分:3)

只需让CoolCollection<T>明确实施IEnumerable<CoolNode<T>>以及IEnumerable<T>。 (我猜它真的是CoolNode<T>,但如果没有,只要把额外的<T>带到各地。)

这将允许您以两种方式进行迭代,尽管您需要演员。

要做到这一点,你需要这样的东西:

class CoolCollection<T> : ICollection<T>, IEnumerable<CoolNode<T>>
{
    IEnumerator<CoolNode<T>> IEnumerable<CoolNode<T>>.GetEnumerator()
    {
        ///...Do work here...
    }

    IEnumerator<T> GetEnumerator()
    {
        ///...Do work here...
    }
}

使用它就像这样:

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


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

另一种选择是公开“节点”的属性,所以你可以这样做:

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

要实现这一点,你会改变一些事情。你需要创建一个实现枚举器的私有类......类似于:

class CoolCollection<T> : ICollection<T>
{
    private List<CoolNode<T>> nodes;

    IEnumerable<CoolNode<T>> Nodes
    {
        get 
        {
             foreach(var node in this.nodes) { yield return node; }
        }
    }
}

答案 1 :(得分:2)

如果我正确理解了这个问题......

你可以这样做,与其他一些集合对象类似:

例如:

foreach (int key in IDictionary.Keys)
{

}

foreach (object value in IDictionary.Values)
{

}

但我认为没有办法按照你所写的方式做到......

答案 2 :(得分:1)

不,你不能这样做。您不能重载默认迭代器。

想象一下,如果你可以重载你的默认迭代器。

这会怎么做? foreach (object o in foo),没有合理的方法来选择合适的迭代器。

你可以做的是有另一个名为ForEach2的方法,它以不同的方式遍历你的集合。或者您可以显式实现接口。或者你可以使用Linq组合来做这种事情。

从课堂设计的角度来看:

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

}

EachPair的LINQ扩展

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

测试代码:

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

答案 3 :(得分:0)

如果CoolCollection实现IEnumerable,你可以写:

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

或者如果T是CoolNode

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

如果您需要以某种方式将每个项目转换为您的类型,您可以使用Linq Select运算符:

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

答案 4 :(得分:0)

查看iterindex代码段。在您的班级中,输入iterindex并点击[TAB]。它将帮助您实现“命名迭代器和索引器”模式。

结果可以像这样使用:

foreach (var e in myTree.DepthFirstView) // supports iteration
{
    if (e == myTree.DepthFirstView[2]) // supports indexing
    {
        // ...
    }
}

(我写了这个片段,但我怀疑它从来没有得到很好的使用。永远。)