.NET IEnumerator<字符串>在收益块中时不继续 MoveNext字符串>
下面的代码(用于在 LinqPad 中运行)旨在将“foo/skip/bar”字符串解析为项目对象,跳过“skip”位,生成“foo”和“bar”的项目对象。运行时,会生成 2 个“条形”项目。
在 TryGetChild 方法中,当找到“skip”时,枚举数从“skip”向前移动到“bar”。但是,当执行返回到调用方法时,枚举器又回到“跳过”状态。
我认为这是一些yield 块的奇怪之处,就好像我在 Main() 中进行拆分并将枚举器传递到 Walk() 中一样,它可以正常工作。有人能解释一下枚举器是如何返回的吗?是否正在创建一个新的?
编辑:这是我在代码中发现的看似奇怪的情况的非常简化的版本。我出于好奇而问这个问题,而不是寻找我已经找到的解决方法。
/* output from program
enumerator moved to foo
enumerator moved to skip
enumerator moved to bar
enumerator moved to bar
Item [] (3 items)
foo
bar
bar
*/
static void Main()
{
Walk("foo/skip/bar").ToArray().Dump();
}
private static IEnumerable<Item> Walk(string pathString)
{
var enumerator = pathString.Split('/').ToList().GetEnumerator();
var current = new Item() { S = "" };
while (enumerator.MoveNext())
{
Console.WriteLine("enumerator moved to " + enumerator.Current);
yield return current.TryGetChild(enumerator);
}
}
class Item
{
public string S { get; set; }
public Item TryGetChild(IEnumerator<string> enumerator)
{
if (enumerator.Current == "skip")
{
enumerator.MoveNext(); //iterator moves on to 123
Console.WriteLine("enumerator moved to " + enumerator.Current);
}
return new Item() { S = enumerator.Current };
}
}
The code below (for running in LinqPad) is meant to parse the "foo/skip/bar" string into item objects, skipping over the 'skip' bit, yielding Item objects for "foo" and "bar". When run, 2 "bar" items are produced.
In the TryGetChild method, when "skip" is found, the enumerator is moved from "skip" forward to "bar". However, when execution returns into the calling method, the enumerator is back on "skip".
I think this is some yield block weirdness, as if I do the split in Main() and pass the enumerator into Walk() it works properly. Can someone explain how the enumerator goes back? Is a new one being created?
edit: This is a very simplified version of the seemingly odd situation I have found in my code. I am asking this question out of inquisitiveness rather than looking for a workaround, which I have already found.
/* output from program
enumerator moved to foo
enumerator moved to skip
enumerator moved to bar
enumerator moved to bar
Item [] (3 items)
foo
bar
bar
*/
static void Main()
{
Walk("foo/skip/bar").ToArray().Dump();
}
private static IEnumerable<Item> Walk(string pathString)
{
var enumerator = pathString.Split('/').ToList().GetEnumerator();
var current = new Item() { S = "" };
while (enumerator.MoveNext())
{
Console.WriteLine("enumerator moved to " + enumerator.Current);
yield return current.TryGetChild(enumerator);
}
}
class Item
{
public string S { get; set; }
public Item TryGetChild(IEnumerator<string> enumerator)
{
if (enumerator.Current == "skip")
{
enumerator.MoveNext(); //iterator moves on to 123
Console.WriteLine("enumerator moved to " + enumerator.Current);
}
return new Item() { S = enumerator.Current };
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您看到此行为的原因是
List.GetEnumerator()
返回List.Enumerator
的实例,它是一个 struct。因此,您将值类型传递给TryGetChild()
方法,并且该类型上的任何突变(包括由MoveNext()
完成的突变)将不会 反映在调用者中。The reason you see this behaviour is that
List<T>.GetEnumerator()
returns an instance ofList<T>.Enumerator
, which is a struct. Thus you are passing a value type to theTryGetChild()
method, and any mutations on that type (including those done byMoveNext()
) will not be reflected in the caller.我认为
yield
仅在使用IEnumerable
或IEnumerable
循环时才有效。I think
yield
only works when using anIEnumerable
orIEnumerable<T>
loop.