嵌套的 IEnumerators 没有按预期运行

问题描述 投票:0回答:3

如果我多次调用

Run()
,我希望看到消息“一”、“二”和“三”,但实际情况并非如此,我可以看到第一个,然后
MoveNext()
返回 false。我在这里错过了什么?

public class Test
{
    private IEnumerator<Object> routine;

    public void Setup()
    {
        routine = CountOne();
    }
    
    public void Run()
    {
        routine.MoveNext();
    }
    
    private IEnumerator<Object> CountOne()
    {
        Console.WriteLine("One");
        yield return CountTwo();
    }

    private IEnumerator<Object> CountTwo()
    {
        Console.WriteLine("Two");
        yield return CountThree();
    }

    private IEnumerator<Object> CountThree()
    {
        Console.WriteLine("Three");
        yield return null;
    }
}
c# ienumerator
3个回答
2
投票

避免使用

Object
:

可能有助于更好地理解事物
public class Test
{
    private IEnumerator<int> routine;

    public void Setup()
    {
        routine = CountOne();
    }

    public void Run()
    {
        routine.MoveNext();
    }

    private IEnumerator<int> CountOne()
    {
        Console.WriteLine("One");
        yield return CountTwo(); // Cannot implicitly convert type ...
    }

    private IEnumerator<int> CountTwo()
    {
        Console.WriteLine("Two");
        yield return CountThree(); // Cannot implicitly convert type ...
    }

    private IEnumerator<int> CountThree()
    {
        Console.WriteLine("Three");
        yield return 0;
    }
}

当您尝试

yield return CountTwo();
yield return CountThree();
时,这会出现一些编译时错误。那是因为这些函数返回
IEnumerable<int>
s 并且您只能从返回
yield return
的方法中执行
int
IEnumerator<int>

您的原始代码中没有发生该错误,因为从技术上讲,

IEnumerator<Object>
是一个
Object
并且可以转换为一个,即使这不是您
yield return
ed 时的意图。

要获得您正在寻找的行为,请将这些从

yield return
更改为仅
return
,或者创建一个
foreach
循环到
yield return
从这些方法返回的 IEnumerator 中的每个项目


0
投票

这似乎来自 Unity,我有一段时间没有使用它,但请尝试枚举枚举器:

public class Test
{
    private IEnumerator routine;
    
    public void Run()
    {
        routine ??= CountOne();

        while (routine.MoveNext())
        {
            // no-op
        }
    }
    
    private IEnumerator CountOne()
    {
        Debug.Log($"One");
        var countTwo = CountTwo();
        while (countTwo.MoveNext())
        {
            yield return countTwo.Current;
        }
    }

    private IEnumerator CountTwo()
    {
        Debug.Log($"Two");
        var countThree = CountThree();
        while (countThree.MoveNext())
        {
            yield return countThree.Current;
        }
    }

    private IEnumerator CountThree()
    {
        Debug.Log($"Three");
        yield return null;
    }
}

否则,在

CountOne
中,您只需返回一个作为下一个枚举器的项目,而无需实际处理它(即
CountOne
的返回类型有效
IEnumerator<IEnumerator>
)。

yield return
支持交替返回
IEnumerator
IEnumerable
。来自文档

迭代器的调用不会立即执行它...

...当您开始迭代迭代器的结果时,将执行迭代器直到到达第一个 yield return 语句。然后,暂停迭代器的执行,调用者获取第一个迭代值并对其进行处理。在每个后续迭代中,迭代器的执行在导致先前暂停的 yield return 语句之后恢复,并继续直到到达下一个 yield return 语句。当控制到达迭代器或 yield break 语句的末尾时,迭代完成。


-1
投票

这里发生的事情是

yield
关键字与方法声明的返回类型混淆。

当我们将

yield
放入声明返回
IEnumerator<Object>
的方法中时,它期望
yield
表达式的结果为
Object
。如果我们将
yield
放在声明返回
IEnumerator<string>
的方法中,(如“一”、“二”、“三”),它期望
yield
表达式的结果为
string
(不是
IEnumerator<string>

但这不是你所拥有的。 相反,嵌套的

CountTwo()
CountThree()
方法也返回
IEnumerator
类型,原始代码编译的唯一原因是这些类型可分配给
Object

同样重要的是,这是三个不同的枚举器,而不是同一枚举器中的三个收益率。当代码第一次调用

Run()
时,只涉及
CountOne()
的枚举器,此时它已完成,因为它只有一个元素。

如果我多次调用 Run(),我希望看到消息“一”、“二”和“三”

让我们实现这一目标!这里有两个例子:

public class Test
{
    private IEnumerator routine;
    
    public void Setup()
    {
        routine = (new string[] {"One", "Two", "Three"}).GetEnumerator();
    }
    
    public void Run()
    {
        routine.MoveNext();
        Console.WriteLine(routine.Current);
    }
}

// Designed to look more like the original
public class Test2
{
    private IEnumerator routine;
    
    public void Setup()
    {
        routine = CountOne();
    }
    
    public void Run()
    {
        routine.MoveNext();

        // queue up the next enumerator
        routine = (IEnumerator)routine.Current;
    }
    
    public IEnumerator<IEnumerator<IEnumerator<string>>> CountOne()
    {
        Console.WriteLine("One");
        yield return CountTwo();
    }
    
    public IEnumerator<IEnumerator<string>> CountTwo()
    {
        Console.WriteLine("Two");   
        yield return CountThree();
    }
    
    public IEnumerator<string> CountThree()
    {
        Console.WriteLine("Three");
        yield return null;
    }
}

看到他们在这里工作:

https://dotnetfiddle.net/08hfBz

© www.soinside.com 2019 - 2024. All rights reserved.