为什么我们不能在C#中使用IteratorStateMachineAttribute?

时间:2016-05-23 09:24:29

标签: c# iterator state-machine compiler-services

我在我试图派生的类上做了一个Go To Definition( F12 ),我注意到其中一个方法标有AsyncStateMachineAttribute。而这又继承了StateMachineAttribute。我很好奇并决定在MSDN上阅读这个属性及其所有衍生物。这导致我this,我发现了这句话:

  

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

因为这个陈述是突出的,所以必须有严重的含义,但没有进一步解释为什么会这样。有没有人有这方面的见解?

3 个答案:

答案 0 :(得分:8)

我99%肯定它的历史。基本上,C#在引入此属性之前在C#2中引入了迭代器块 - long 时间。

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

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

我会说方法上存在IteratorStateMachineAttribute是一个很好的指标,它 是一个迭代器方法(虽然没有什么可以阻止恶作剧的开发人员申请它适用于其他方法),但由于旧版本的C#编译器,它并不是一个充分的测试。

答案 1 :(得分:3)

这里的状态机是由C#编译器自动生成的。在继续之前,C#编译器在内部将许多高级功能(如闭包,yield关键字和async)转换为简化的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'本身 - 它已被简化为整数和返回语句。我认为这是添加IteratorStateMachineAttribute的地方 - 对于类的简化,整理和返回版本。

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

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

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

答案 2 :(得分:0)

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