使用yield的方法不允许调用自己
这很可能是用户错误(我有点希望如此)。我在 C# 中遇到了一个奇怪的情况,如果我尝试在使用 Yield 的方法中进行递归调用,它似乎不会受到尊重(即调用被忽略)。
下面的程序说明了这一点:
// node in an n-ary tree
class Node
{
public string Name { get; set; }
public List<Node> ChildNodes { get; set; }
}
class Program
{
// walk tree returning all names
static IEnumerable<string> GetAllNames(IEnumerable<Node> nodes)
{
foreach (var node in nodes)
{
if (node.ChildNodes != null)
{
Console.WriteLine("[Debug] entering recursive case");
// recursive case, yield all child node names
GetAllNames(node.ChildNodes);
}
// yield current name
yield return node.Name;
}
}
static void Main(string[] args)
{
// initalize tree structure
var tree = new List<Node>
{
new Node()
{
Name = "One",
ChildNodes = new List<Node>()
{
new Node() {Name = "Two"},
new Node() {Name = "Three"},
new Node() {Name = "Four"},
}
},
new Node() {Name = "Five"}
};
// try and get all names
var names = GetAllNames(tree);
Console.WriteLine(names.Count());
// prints 2, I would expect it to print 5
}
}
This could well be a user error (I'm kinda hoping so). I'm running into a strange case in C# were if I try to make recursive call in a method that uses yield it doesn't seem to be respected (i.e. the call is ignored).
The following program illustrates this:
// node in an n-ary tree
class Node
{
public string Name { get; set; }
public List<Node> ChildNodes { get; set; }
}
class Program
{
// walk tree returning all names
static IEnumerable<string> GetAllNames(IEnumerable<Node> nodes)
{
foreach (var node in nodes)
{
if (node.ChildNodes != null)
{
Console.WriteLine("[Debug] entering recursive case");
// recursive case, yield all child node names
GetAllNames(node.ChildNodes);
}
// yield current name
yield return node.Name;
}
}
static void Main(string[] args)
{
// initalize tree structure
var tree = new List<Node>
{
new Node()
{
Name = "One",
ChildNodes = new List<Node>()
{
new Node() {Name = "Two"},
new Node() {Name = "Three"},
new Node() {Name = "Four"},
}
},
new Node() {Name = "Five"}
};
// try and get all names
var names = GetAllNames(tree);
Console.WriteLine(names.Count());
// prints 2, I would expect it to print 5
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您正在拨打电话,但没有执行任何操作。您需要在这里实际使用结果
You are making the call but doing nothing with it. You need to actually use the result here
您没有返回递归调用的结果。
您需要
yield return
从调用返回的每个项目:You aren't returning the results of the recursive call.
You need to
yield return
each item returned from the call:这是一个非常有趣的问题,可能会导致任意深度的结构占用大量资源。我认为斯基特先生提出了一种“扁平化”技术,但我不记得这个链接了。这是我们根据他的想法使用的代码(它是 IEnumerable 的扩展方法):
这避免了递归和大量嵌套迭代器。然后您可以这样称呼它:
现在这是一个“预购订单”,其中节点名称排在第一位,但如果您愿意,您可以轻松编写后购订单。
This is a very interesting problem that can result in A LOT of resource utilization for arbitrarily-deep structures. I think Mr. Skeet proposed a 'flattening' technique but I don't recall the link. Here's the code we use based on his idea (it's an extension method on IEnumerable):
This avoids recursion and a lot of nested iterators. You would then call it:
Now this IS a 'pre-order' where the node name comes first, but you could easily write a post-order if that's what you prefer.