What Does "Displayclass" Name Mean When Calling Lambda

What does DisplayClass name mean when calling lambda?

From an answer to a related question by Eric Lippert:

The reason that a closure class is called "DisplayClass" is a bit unfortunate: this is jargon used by the debugger team to describe a class that has special behaviours when displayed in the debugger. Obviously we do not want to display "x" as a field of an impossibly-named class when you are debugging your code; rather, you want it to look like any other local variable. There is special gear in the debugger to handle doing so for this kind of display class. It probably should have been called "ClosureClass" instead, to make it easier to read disassembly.

Why are DisplayClass and calling method name ordered this way in the stack trace?

<implMain>b__0() is just the name of the method.

Inspection in a disassembler will show you this. The < and > does not imply generics.

The inclusion of implMain probably just hints where the delegate was created.

Why Does Code Generate MSIL Class Called c__DisplayClass1

If you use local variables in a lambda it needs to be on the heap. The lambda might be used after the function which created it exits. Normal local variables (living on the stack/registers) become invalid when the function exits, so they can't be used here.

So the C# compiler creates a class to hold captured local variables. That's the one you're seeing.

Note that C# captures the actual variable, not its current value. So conceptually it's captured by reference. The semantics of capturing mean that the compiler needs to create one container object per scope.

http://csharpindepth.com/Articles/Chapter5/Closures.aspx


In your code

x =>
{
return x.StartMetres == startMetres && x.EndMetres == divisionPosition;
}

The lambda uses startMetres and divisionPosition, so both of them get captured and are put in that nested class.

The significance of in C#

You're looking at some decompiled code - specifically, something that was generated by the compiler.

The compiler uses <> (this is an implementation detail) because, whilst it's valid for a CLR identifier to start with such characters, it's not valid in C# - so it's guaranteed that the name will not conflict with any names in the C# code.

why the compiler has generated this code varies - it can be the implementation of a lambda, or an iterator or async block, and possibly some other reasons also.


And, hopefully the other part of your question is also answered - there's a . in front of it for the usual reasons - to separate namespace portions, or more likely in this case, to separate the name of a nested class from the name of the enclosing class.

When a variable is closed twice, where is it stored?

There is only one closure class for that method, rather than one per anonymous method. That one class will have two instance methods and a field. The field will store the value of i, and the two methods will each correspond to your two anonymous methods.

Where to learn about VS debugger 'magic names'

These are undocumented implementation details of the compiler, and subject to change at any time. (UPDATE: See GeneratedNames.cs
in the C# sources for the current details; the description below is somewhat out-of-date.)

However, since I'm a nice guy, here are some of those details:

If you have an unused local variable that the optimizer removes, we emit debug info for it anyway into the PDB. We stuck the suffix __Deleted$ onto such variables so that the debugger knows that they were in source code but not represented in the binary.

Temporary variable slots allocated by the compiler are given names with the pattern CS$X$Y, where X is the "temporary kind" and Y is the number of temporaries allocated so far. The temporary kinds are:

0 --> short lived temporaries
1 --> return value temporaries
2 --> temporaries generated for lock statements
3 --> temporaries generated for using statements
4 --> durable temporaries
5 --> the result of get enumerator in a foreach
6 --> the array storage in a foreach
7 --> the array index storage in a foreach.

Temporary kinds between 8 and 264 are additional array index storages for multidimensional arrays.

Temporary kinds above 264 are used for temporaries involving the fixed statement fixing a string.

Special compiler-generated names are generated for:

1 --> the iterator state ("state")
2 --> the value of current in an iterator ("current")
3 --> a saved parameter in an iterator
4 --> a hoisted 'this' in an iterator ("this")
5 --> a hoisted local in an iterator
6 --> the hoisted locals from an outer scope
7 --> a hoisted wrapped value ("wrap")
8 --> the closure class instance ("locals")
9 --> the cached delegate instance ("CachedAnonymousMethodDelegate")
a --> the iterator instance ("iterator")
b --> an anonymous method
c --> anonymous method closure class ("DisplayClass")
d --> iterator class
e --> fixed buffer struct ("FixedBuffer")
f --> anonymous type ("AnonymousType")
g --> initializer local ("initLocal")
h --> query expression temporary ("TransparentIdentifier")
i --> anonymous type field ("Field")
j --> anonymous type type parameter ("TPar")
k --> auto prop field ("BackingField")
l --> iterator thread id
m --> iterator finally ("Finally")
n --> fabricated method ("FabricatedMethod")
o --> dynamic container class ("SiteContainer")
p --> dynamic call site ("Site")
q --> dynamic delegate ("SiteDelegate")
r --> com ref call local ("ComRefCallLocal")
s --> lock taken local ("LockTaken")

The pattern for generating magical names is: P<N>C__SI where:

  • P is CS$ for cached delegates and display class instances, empty otherwise.
  • N is the original name associated with the thing, if any
  • C is the character 1 through s listed above
  • S is a descriptive suffix ("current", "state", and so on) so that you don't have to have the table above memorized when reading the metadata.
  • I is an optional unique number

Is this closure combination behaviour a C# compiler bug?

Is this a bug?

No. The compiler is compliant with the specification here.

Why is this behaviour considered desirable?

It's not desirable. It's deeply unfortunate, as you discovered here, and as I described back in 2007:

http://blogs.msdn.com/b/ericlippert/archive/2007/06/06/fyi-c-and-vb-closures-are-per-scope.aspx

The C# compiler team has considered fixing this in every version since C# 3.0 and it has never been high enough priority. Consider entering an issue on the Roslyn github site (if there isn't one already; there may well be).

I personally would like to see this fixed; as it stands it is a big "gotcha".

How do I code against this without abandoning the use of lambdas all together?

The variable is the thing that is captured. You could set the hashset variable to null when you're done with it. Then the only memory being consumed is the memory for the variable, four bytes, and not the memory for the thing it is referring to, which will be collected.

Accessing property on DisplayClass object at runtime

The compiler generates closure types with fields rather than properties, so id is a field. Use tgt.GetType().GetFields() instead.

Getting root of expression

I have found the complete solution to my answer which has two parts:

  1. Why the DisplayClass and what it means can be found in the answer to this question: What does “DisplayClass” name mean when calling lambda?
  2. How to get the actual root of the expression (which is possible by the way) can be found in this questions answer: Getting the object out of a MemberExpression.


Related Topics



Leave a reply



Submit