枚举不是固有的IEnumerable集合? [英] Enumerating Collections that are not inherently IEnumerable?

查看:248
本文介绍了枚举不是固有的IEnumerable集合?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当你想递归枚举分层对象,选择基于一些标准的一些元素,有喜欢的扁平化,然后使用LINQ过滤技术的例子不胜枚举:像那些在这里找到:

When you want to recursively enumerate a hierarchical object, selecting some elements based on some criteria, there are numerous examples of techniques like "flattening" and then filtering using Linq : like those found here :

链接文本

不过,当你列举类似的控件集合的形式,或者一个TreeView的节点集合,我已经无法使用这些类型的技术,因为他们似乎需要一个参数(与扩展方法),这是IEnumerable集合:传递SomeForm.Controls不编译

But, when you are enumerating something like the Controls collection of a Form, or the Nodes collection of a TreeView, I have been unable to use these types of techniques because they seem to require an argument (to the extension method) which is an IEnumerable collection : passing in SomeForm.Controls does not compile.

我发现的最有用的东西是这样的:

The most useful thing I found was this :

<一个href=\"http://blogs.windowsclient.net/rendle/archive/2008/03/06/recursing-controlcollection.aspx\">link文字

这不给你Control.ControlCollection的扩展方法有一个IEnumerable的结果,那么你可以使用LINQ使用。

Which does give you an extension method for Control.ControlCollection with an IEnumerable result you can then use with Linq.

我修改了上面的例子没有问题解析一个TreeView的节点。

I've modified the above example to parse the Nodes of a TreeView with no problem.

public static IEnumerable<TreeNode> GetNodesRecursively(this TreeNodeCollection nodeCollection)
{
    foreach (TreeNode theNode in nodeCollection)
    {
        yield return theNode;

        if (theNode.Nodes.Count > 0)
        {
            foreach (TreeNode subNode in theNode.Nodes.GetNodesRecursively())
            {
                yield return subNode;
            }
        }
    }
}

这是那种code现在我正在写使用扩展的方法:

This is the kind of code I'm writing now using the extension method :

    var theNodes = treeView1.Nodes.GetNodesRecursively();

    var filteredNodes = 
    (
        from n in theNodes
            where n.Text.Contains("1")
                select n
    ).ToList();

我认为有可能是一个更优雅的方式来做到这一点,其中的约束(收费)传递。

And I think there may be a more elegant way to do this where the constraint(s) are passed in.

我想知道什么,如果它是可能的一般定义这样的程序,使得:在运行时,我可以在集合的类型传递,以及实际的集合,向一个通用的参数,所以code是独立于它是否是一个TreeNodeCollection或Controls.Collection。

What I want to know if it is possible to define such procedures generically, so that : at run-time I can pass in the type of collection, as well as the actual collection, to a generic parameter, so the code is independent of whether it's a TreeNodeCollection or Controls.Collection.

这也将我感兴趣知道是否有任何其他方式(更便宜?fastser?)比第二个链接(上图)所示获得通过的Linq使用的形式一TreeNodeCollection或Control.ControlCollection。

It would also interest me to know if there's any other way (cheaper ? fastser ?) than that shown in the second link (above) to get a TreeNodeCollection or Control.ControlCollection in a form usable by Linq.

由Leppie约在一个的SelectMany的意见后SO链接到第一(上)似乎是一个线索。

A comment by Leppie about 'SelectMany in the SO post linked to first (above) seems like a clue.

我与实验的SelectMany已经:好,称他们为灾难。 :)

My experiments with SelectMany have been : well, call them "disasters." :)

鸭preciate任何指针。我花了几个小时,每次看完后SO我能找到感动在这些地区,海北我的方式进入这样的新奇的Y型组合子。 A震撼人心的经历,我想补充:)

Appreciate any pointers. I have spent several hours reading every SO post I could find that touched on these areas, and rambling my way into such exotica as the "y-combinator." A "humbling" experience, I might add :)

推荐答案

这code应该做的伎俩

This code should do the trick

public static class Extensions
{
    public static IEnumerable<T> GetRecursively<T>(this IEnumerable collection,
        Func<T, IEnumerable> selector)
    {
        foreach (var item in collection.OfType<T>())
        {
            yield return item;

            IEnumerable<T> children = selector(item).GetRecursively(selector);
            foreach (var child in children)
            {
                yield return child;
            }
        }
    }
}

下面是如何使用它的一个示例

Here's an example of how to use it

TreeView view = new TreeView();

// ...

IEnumerable<TreeNode> nodes = view.Nodes.
    .GetRecursively<TreeNode>(item => item.Nodes);

更新:为了响应埃里克利珀的帖子

Update: In response to Eric Lippert's post.

下面是使用的所有关于迭代器

public static class Extensions
{
    public static IEnumerable<T> GetItems<T>(this IEnumerable collection,
        Func<T, IEnumerable> selector)
    {
        Stack<IEnumerable<T>> stack = new Stack<IEnumerable<T>>();
        stack.Push(collection.OfType<T>());

        while (stack.Count > 0)
        {
            IEnumerable<T> items = stack.Pop();
            foreach (var item in items)
            {
                yield return item;

                IEnumerable<T> children = selector(item).OfType<T>();
                stack.Push(children);
            }
        }
    }
}

我用下面的标杆技术做了一个简单的性能测试。结果不言自明。树的深度只有在第二解决方案的性能边际影响;而性能迅速降低为第一溶液,最终leadning至 StackOverflowException 当树的深度变得过大。

I did a simple performance test using the following benchmarking technique. The results speak for themselves. The depth of the tree has only marginal impact on the performance of the second solution; whereas the performance decreases rapidly for the first solution, eventually leadning to a StackOverflowException when the depth of the tree becomes too great.

这篇关于枚举不是固有的IEnumerable集合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆