Reflector 9.0 Released: What We Learned About C# 6

Last week we released .NET Reflector 9.0. As Ben Emmett explained in his blog post, we’ve been working on support for decompiling the new language features in C# 6 (as well as VB14). You’ll find more details about the release itself in the release notes, and for customers of version 7 or higher you can upgrade for free at http://www.red-gate.com/dynamic/products/dotnet-development/reflector/download.

But what I really want to talk about is what we learned along the way. This post is an overview of the syntax Reflector now supports, with some notes on what we discovered while working on it.

What we learned about C# 6

Null conditional operator, “?.”

This construct is designed to reduce the amount of code needed to implement boilerplate null checks.

Here, the method is only called if mightBeNull is not null. If the method returns an object, then the operator can be chained:

and this led to interesting behaviour in Reflector. Reflector will initially turn the IL behind the statement into a series of ternary operators (actually an internal representation of them), and then reduce that to the chain of null conditional operators by recognising a pattern characteristic of “?.”. However it turns out that this algorithm is exponentially slow in the length of the chain! Let’s see how.

This is Reflector’s first attempt at decoding increasing levels of chaining:

The length of the expression is roughly doubling for each new null conditional operator we use. In the underlying IL, however, we see that the number of instructions increases linearly, with an increase of 12 for each extra step.

What’s going on? The trick is that the .NET runtime can use the stack to create ‘phantom variables’ using the dup instruction, which duplicates the current topmost stack value. It can use the value of this variable to decide whether to jump to the end of the method via the unconditional jump br.s L_0019, and get an early exit. Reflector tries not to create variables that the user didn’t define, and doesn’t have a fake stack, so it ends up creating nested ternary operators instead.

This limitation of Reflector can lead to problems elsewhere too, and is something we’re planning on working on.

Auto property initializers

Exception filters

Indexer initializers

We took time to improve our support for object initializers, including adding support for indexer initializers and tidying up some of the cases we failed to spot previously. The complicated part here is that the IL looks like a creation followed by a list of assignments, which is a set of statements, but we want to turn the set of statements into an expression, which is a completely different beast.

Expression bodied function members and properties

In decompiling existing code we found many places where it was impossible to tell if the original code was an expression bodied function or property, or merely something which could have been written as one. Depending on your Reflector settings, this has the disconcerting effect of making your decompiled code look more up-to-date than you are.

Roslyn-compiled async code and anonymous methods

The Roslyn compiler and the C#5 compiler emit different IL from each other for asynchronous and anonymous methods. Reflector can now correctly decompile Roslyn-compiled code for these language constructs.

The nature of the change to lambda functions (anonymous methods) is quite interesting. When a lambda function references something from outside the lambda’s scope, it captures the value of that thing as a field inside a compiler generated class, a ‘closure’. This is what we’ve all learned as .NET developers (and perhaps seen inside Reflector) but the Roslyn compiler has a subtle change. The name of the compiler generated class is slightly different (and it’s now a nested class), and that was enough to stop Reflector recognising it, leading to the compiler’s impenetrable internals being spewed across the screen. A quick change in IsAnonymousDelegateName() and everything was right again.

For async methods, the story is similar. The async state machine that was previously represented by a compiler generated struct is replaced by a compiler generated class in Debug builds, to support Visual Studio’s edit-and-continue. (ref: this Roslyn comment)

//The CLR doesn't support adding fields to structs, so in order to enable EnC in an async method we need to generate a class.

String interpolation

Getting this right had a twist. It compiles to calls to String.Format, but it turns out that there are overloads for String. Format which look like:

for up to three parameters, but once you go above three, the CLR uses:

which needs to be treated completely differently — a variation on the tester’s “Zero-one-many” rule, which states a rule of thumb for testing collections – in many cases testing zero, one, and more than one element is sufficient.

Finally{}

For further reading on the new features in C# 6 check out Paulo Morgado’s article. On behalf of the team, I must say a big thank you to Paulo for his ongoing feedback during the beta program.

Try the latest release out for yourself, and as always we really appreciate any feedback – you can reach us at dotnetteam@red-gate.com.