4

我在一个我试图派生的类上做了一个 Go To Definition( F12),我注意到其中一个方法用AsyncStateMachineAttribute. 这又继承了StateMachineAttribute. 我很好奇并决定在 MSDN 上阅读此属性及其所有派生词。这导致我这样做 ,我遇到了这个声明:

在 C# 中,您不能使用它IteratorStateMachineAttribute来测试方法是否是迭代器方法。

因为该声明是为了突出,所以它必须有严重的影响,但没有进一步解释为什么会这样。有没有人有这方面的见解?

4

3 回答 3

9

我 99% 确定这是历史性的。基本上,C# 在 C# 2 中引入了迭代器块——在引入此属性之前很长时间。

等效的 async 属性是与 C# 中的异步方法同时引入的,所以这很好......但即使 C# 编译器现在适用IteratorStateMachineAttribute于迭代器块:

  • 它不适用于使用旧版本编译器创建的库,因此您将无法在那里依赖它。
  • 它不适用于面向 .NET 4.5 之前版本的库。(老实说,我不确定 VB 编译器在这里做了什么。它可能会省略该属性,或者它可能需要您以最新版本的 .NET 为目标才能使用迭代器方法。)

我想说IteratorStateMachineAttribute一个方法的存在很好地表明它一个迭代器方法(尽管没有什么可以阻止调皮的开发人员将它应用于其他方法),但由于 C# 的旧版本,它不是一个充分的测试编译器。

于 2016-05-23T09:31:37.520 回答
4

这里的状态机是由 C# 编译器自动生成的。C# 编译器在继续之前在内部将许多高级功能(如闭包、yield 关键字和异步)转换为简化的 C#。诸如“AsyncStateMachineAttribute”之类的事情是发生此类事情的一点证据。您可能还熟悉名为 DisplayClass923084923'1 的类,它们是 C# 为实现闭包而生成的类。

例如,当您使用“yield”时,C# 编译器首先生成一个不使用“yield”而是使用状态机实现的代码版本。原则上,从这里;

yield "A";
yield "B";

int _state = 0;

if (_state == 0) { state = 1; return "A"; }
if (_state == 1) { state = 2; return "B"; }

这意味着 C# 编译器以后不必​​像这样处理“yield” ——它已被简化为整数和 return 语句。我认为这是在IteratorStateMachineAttribute类的简化、整数和返回版本中添加的地方。

(我认为 Async 的工作方式相同,生成一个简化的状态机作为其简化步骤,这就是您在文档中的方式。)

但是,从最早的 C# 版本开始,您就拥有了foreach关键字,该关键字适用于任何具有GetEnumerator方法的对象,并且该枚举器具有诸如MoveNext和之类的方法Result

所以——迭代器方法可能以不同的方式产生。IteratorStateMachineAttribute是编译器在某些情况下提供的,但你不应该依赖它在那里。

于 2016-05-23T09:37:17.100 回答
0

这表明您不能将此标志应用于方法,因为在编译期间它将注入一些不能可靠地添加到方法中的 IL 代码。

于 2016-05-23T09:29:29.393 回答