枚举不是固有的IEnumerable集合? [英] Enumerating Collections that are not inherently 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屋!