4

I did a Go To Definition (F12) on a class I was trying to derive from and I noticed that one of the methods was marked with AsyncStateMachineAttribute. Which in turn inherits StateMachineAttribute. I was curious and decide to read up on this attribute and all its derivates on MSDN. That led me to this and I came across this statement:

You can't use IteratorStateMachineAttribute to test whether a method is an iterator method in C#.

Because that statement is made to stand out, there must be serious implications to it but there is no further explanation as to why that is so. Does anyone have insights in this regard?

Nasreddine
  • 36,610
  • 17
  • 75
  • 94
Eniola
  • 710
  • 1
  • 7
  • 23
  • 1
    I think it's just trying to say that you can't _rely_ on the attribute to test for such methods in a reflection context (e.g. by a static analyser). – Martin Costello May 23 '16 at 09:30
  • It is highly specific to VB.NET. Tooling that converts metadata back into a VB.NET declaration (like Go To Definition) uses it to know that the Function needs to be displayed with the Iterator keyword. Without that help an Iterator Function() As T would show up as Function() As IEnumerable(Of T). The C# compiler now emits it as well, it did not do so before VB.NET got iterator support in VS2012. So you can't depend on it. – Hans Passant May 23 '16 at 10:03

3 Answers3

9

I'm 99% sure it's historical. Basically, C# introduced iterator blocks in C# 2 - a long time before this attribute was introduced.

The equivalent async attribute was introduced at the same time as async methods in C#, so that was fine... but even though the C# compiler now applies IteratorStateMachineAttribute to iterator blocks:

  • It doesn't apply to libraries created with older versions of the compiler, so you wouldn't be able to rely on it there.
  • It can't apply to libraries targeting versions of .NET prior to 4.5. (I'm not sure what the VB compiler does here, to be honest. It may omit the attribute, or it may require you to be targeting a recent version of .NET in order to use iterator methods.)

I would say that the presence of an IteratorStateMachineAttribute on a method is a good indicator that it is an iterator method (although there's nothing to stop a mischievous developer applying it to other methods), but it's not a sufficient test due to older versions of the C# compiler.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
4

The State Machine here is one that is automatically generated by the C# compiler. The C# compiler internally converts lots of advanced features (like closures, the yield keyword, and async) into simplified C# before continuing. Things like 'AsyncStateMachineAttribute' is one bit of evidence that something like this has occured. You might also be familiar with classes called, e.g., DisplayClass923084923'1, which are the classes C# generates to implement closures.

When you use 'yield', say, the C# compiler first produces a version of the code which doesn't use 'yield' but instead uses a state machine implementation. In principle, from this;

yield "A";
yield "B";

to

int _state = 0;

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

This means the C# compiler, later on, doesn't have to deal with 'yield' as such -- it's been reduced to integers and return statements. I think this is where the IteratorStateMachineAttribute is added -- to the simplified, ints-and-returns version of the class.

(I think Async works the same way, producing a simplified state machine as its simplification step, which is how you came to it in the documentation.)

However, ever since the earliest version of C#, you've had the foreach keyword, which works on any object that has a GetEnumerator method, and that enumerator has methods like MoveNext and Result.

So -- an iterator method might be produced in different ways. IteratorStateMachineAttribute is what's supplied by the compiler in some cases, but you shouldn't rely on it being there.

Steve Cooper
  • 20,542
  • 15
  • 71
  • 88
0

This is informing that you cannot apply this flag to a method because during the compilation it will inject some IL code that cannot be reliably added to methods.

Linvi
  • 2,077
  • 1
  • 14
  • 28