繁体   English   中英

为什么编译器生成的 state 机器反复将 state 恢复为 -1?

[英]Why the compiler-generated state machine restores repeatedly the state to -1?

我试图了解迭代器如何在内部工作,以减轻我对线程安全的一些担忧。 让我们考虑例如以下简单的迭代器:

using System.Collections.Generic;

public class MyClass
{
    public static IEnumerable<int> MyMethod()
    {
        yield return 10;
        yield return 20;
        yield return 30;
    }
}

在将此代码复制粘贴到SharpLab.io后,我可以看到在幕后创建的编译器生成的 state 机器。 它是实现接口IEnumerable<int>IEnumerator<int>的 class ,并包含以下MoveNext方法:

private bool MoveNext()
{
    switch (<>1__state)
    {
        default:
            return false;
        case 0:
            <>1__state = -1;
            <>2__current = 10;
            <>1__state = 1;
            return true;
        case 1:
            <>1__state = -1;
            <>2__current = 20;
            <>1__state = 2;
            return true;
        case 2:
            <>1__state = -1;
            <>2__current = 30;
            <>1__state = 3;
            return true;
        case 3:
            <>1__state = -1;
            return false;
    }
}

标识符<>1__state<>2__current是此 class 的私有字段:

private int <>1__state;
private int <>2__current;

我注意到这段代码中有一个模式。 首先将<>1__state字段的值恢复为 -1,然后将<>2__current分配给下一个迭代值,然后将<>1__state推进到下一个 state。 我的问题是: <>1__state = -1;的目的是什么? 线? 我编译了这段代码(在痛苦地重命名了所有非法标识符之后)并确认可以注释掉这一行而不影响 class 的功能。 我不相信 C# 编译器团队只是忘记了这段看似毫无目的的代码。 它的存在肯定是有目的的,我想知道这个目的是什么。

关于为什么您需要一个 state 变量并将其设置为 -1 每次输入您的 switch 语句,没有一个明确的答案。 但我可以想到一个你真的需要这个变量的例子。

就像我在评论部分所说的那样,编译器不知道也不关心 <>2__current 做了什么。

下载文件可能是一个长期运行的 web 请求。 它可能是计算的结果,也可能只是您的示例中的 integer 。 但问题就在这里,因为编译器不知道你的代码做了什么,它可能会抛出异常。 让我们看一个例子,如果你省略 _state 变量会发生什么,你会在尝试下载某些东西时遇到异常。

1) MoveNext is called.
2) this.<>2_current = WebRequest.GetFileAsync() throws HttpRequestException.
3) The exception is caught somewhere and the execution of the program is resumed.
4) The caller invokes MoveNext method.
5) this.<>2_current = WebRequest.GetFileAsync() throws HttpRequestException

所以在这种情况下,我们会陷入一个循环,因为只有在成功下载该数据后才会更改 state。

当我们引入 _state 变量时,结果看起来有很大不同。

1) MoveNext is called.
2) this.<>2_current = WebRequest.GetFileAsync() throws HttpRequestException.
3) The exception is caught somewhere and execution of the program is resumed.
4) The caller invokes MoveNext method.

5) Since there’s no switch case for -1, the default block is reached which informs about the end of a sequence.

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM