0

I'm trying to get a deeper understanding of each frame in a .NET stack trace. Most stack traces are easy to understand. I also know about packages like Ben.Demystifier to make it even more readable. Here's an example of a stack frame I'm trying to understand better:

Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()

As I understand this, the Microsoft.AspNetCore.Mvc.Controllers part is the namespace, ControllerBinderDelegateProvider is a class, and MoveNext() is a method. What about the <>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d part can someone help explain what this cover?

ThomasArdal
  • 4,999
  • 4
  • 33
  • 73

1 Answers1

6

The <>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d part is generated by the C# compiler. In the cited case, it is specifically generated for the local function Bind in the method CreateBinderDelegate that belongs to the class ControllerBinderDelegateProvider, and which sits under the namespace Microsoft.AspNetCore.Mvc.Controllers.

To understand the various part of the name, follow GeneratedNames.MakeLocalFunctionName in the compiler/Roslyn source code. It calls the main workhorse method MakeMethodScopedSynthesizedName.

The implementation details may change over time, but for now, here's a rough breakdown of <>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d, which you can see as being composed of several parts:

  1. <>c__DisplayClass0_0
  2. <CreateBinderDelegate>g__Bind|0
  3. <...>d or <<CreateBinderDelegate>g__Bind|0>d

Note that in the third case, the second one is embedded.

All the synthesized names created by MakeMethodScopedSynthesizedName have the following pattern based on parameter names:

"<" methodName? ">" kind
  ( "__" suffix suffixTerminator? )?
  ( methodOrdinal ("#" methodGeneration )?
  | entityOrdinal ("#" entityGeneration )?
  | methodOrdinal ("#" methodGeneration )?
    "_"
    entityOrdinal ("#" entityGeneration )?
  )?

In the above pattern, anything in quotes ("...") is a literal, parentheses ((...)) are used for grouping, ? means optional, | means or, and identifiers like methodName and suffix represent the value of MakeMethodScopedSynthesizedName arguments.

The <>c__DisplayClass0_0 part is generated by a call to GeneratedNames.MakeLambdaDisplayClassName:

MakeMethodScopedSynthesizedName(GeneratedNameKind.LambdaDisplayClass,
                                methodOrdinal, generation,
                                suffix: "DisplayClass",
                                entityOrdinal: closureOrdinal,
                                entityGeneration: closureGeneration);

Since methodName is null, the synthesized name starts with <>, the c comes from GeneratedNameKind.LambdaDisplayClass, the suffix is clearly DisplayClass and the 0_0 comes from assuming that methodOrdinal and generation are zero for that call.

You can follow the sources to see how the other parts are built with a variation of arguments to MakeMethodScopedSynthesizedName, but in brief:

  • The <CreateBinderDelegate>g__Bind|0 part is generated by a call to GeneratedNames.MakeLocalFunctionName.
    MakeMethodScopedSynthesizedName(GeneratedNameKind.LocalFunction, methodOrdinal, methodGeneration, methodName, localFunctionName, GeneratedNameConstants.LocalFunctionNameTerminator, lambdaOrdinal, lambdaGeneration);
    
  • The <...>d part is generated by GeneratedNames.MakeStateMachineTypeName where the ... comes from the methodName value:
    MakeMethodScopedSynthesizedName(GeneratedNameKind.StateMachineType, methodOrdinal, generation, methodName);
    
Atif Aziz
  • 36,108
  • 16
  • 64
  • 74