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

4
我在尝试继承一个类时,用了Go To Definition(F12),发现其中一个方法标有AsyncStateMachineAttribute,它继承了StateMachineAttribute。我很好奇,于是去MSDN上阅读了这个属性及其派生类的相关信息,然后看到这里并读到如下声明:

你无法使用IteratorStateMachineAttribute来测试C#中的迭代方法。

由于这个声明比较突出,因此可能会有严重的影响,但是没有进一步的解释说明原因。是否有人对此有深入见解?

1
我认为它只是想表达在反射上下文中(例如通过静态分析器),你不能依赖于属性来测试这样的方法。 - Martin Costello
这对VB.NET非常特定。将元数据转换回VB.NET声明的工具(如Go To Definition)使用它来知道函数需要显示为带有Iterator关键字的。如果没有这个帮助,Iterator Function() As T将显示为Function() As IEnumerable(Of T)。C#编译器现在也会发出它,而以前在VB.NET在VS2012中获得迭代器支持之前不会这样做。所以你不能依赖它。 - Hans Passant
3个回答

9
我99%确定这是历史性的。基本上,C#在C# 2中引入了迭代器块,比引入这个属性要早得多。
等效的async属性是在C#同时引入异步方法时引入的,所以这很好……但是尽管C#编译器现在将IteratorStateMachineAttribute应用于迭代器块:
- 它不适用于使用较旧版本编译器创建的库,因此您无法在那里依赖它。 - 它无法应用于针对4.5之前的.NET版本的库。(说实话,我不知道VB编译器在这里做什么。它可能省略该属性,或者可能要求您针对最新版本的.NET才能使用迭代器方法。)
我认为,在方法上存在IteratorStateMachineAttribute是一个很好的指示符,表明它是一个迭代器方法(尽管还是有调皮开发人员将其应用于其他方法的可能),但由于C#编译器的旧版本,这并不是一个充分的测试。

4
这里的状态机是由C#编译器自动生成的。在继续之前,C#编译器会内部地将许多高级功能(如闭包、yield关键字和async)转换为简化的C#。类似于“AsyncStateMachineAttribute”这样的东西就是一个证据。您可能还熟悉名为“DisplayClass923084923'1”的类,这些类是C#生成用于实现闭包的。
例如,当您使用“yield”时,C#编译器首先生成一份不使用“yield”的代码版本,而是使用状态机实现。原则上,从这个版本中;
yield "A";
yield "B";

to

int _state = 0;

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

这意味着C#编译器在之后就不必像处理‘yield’一样去处理它 - 它已经被转化为整数和返回语句。我认为这就是添加IteratorStateMachineAttribute的地方 - 添加到简化后只有整数和返回语句版本的类中。
(我认为异步工作方式也是一样的,通过其简化步骤产生一个简化的状态机,这就是你在文档中找到它的原因。)
然而,自C#的早期版本以来,你就拥有了foreach关键字,它适用于任何具有GetEnumerator方法的对象,并且该枚举器具有像MoveNextResult这样的方法。
所以 - 一个迭代器方法可能以不同的方式产生。IteratorStateMachineAttribute在某些情况下由编译器提供,但你不应该依赖它的存在。

0

这是告知您不能将此标志应用于方法,因为在编译过程中,它将注入一些IL代码,这些代码无法可靠地添加到方法中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接