.NET Tools ReSharper Platform

ReSharper IL Viewer and Low-Level C#

The C# programming language is jam-packed with so much syntactic sugar it would make your dentist mad. Its high-level features allow you to write terse expressions that typically require several lines of code in lower-level languages such as C. How do C# language designers keep packing in new language features while allowing you to target older runtimes and keeping your applications performant? Well, it’s all about Lowering. 

This post will discuss “lowering” and how ReSharper can help you uncover the magic behind the curtain.

What Is Lowering?

When you write C# code, your ultimate goal is to compile the code to a runnable artifact, but it’s rarely a simple one-step process. Most modern C# codebases include high-level keywords such as foreach, var, using, async, and await. You can look at these keywords as shorthand for other basic language building blocks. For example, you could easily interpret a foreach block into a lower version of a for block. 

Lowering transforms high-level language features into “simpler” techniques within the same language. While you could do this manually, many of these translations automatically happen during the build process. This process occurs before the compiler begins translating C# into the Intermediate Language (IL) used by the .NET Runtime.

Now that you have a general idea of “lowering”, let’s use ReSharper to see some of these high-level features in their lower forms.

Lowering Examples

Let’s start with the humble foreach loop, likely used by millions of developers across the .NET Landscape.

var numbers = 
  Enumerable.Range(1, 100);

foreach (var number in numbers)
{
  Console.WriteLine(number);
}

Looking at ReSharper’s IL Viewer, we can choose between three viewing states: IL, Low-Level C#, and High-Level C#. As you can see in the following screenshot, .NET lowered the foreach implementation into a combination of while and try/catch constructs.

Lowering example in Visual Studio 2022 and ReSharper's IL Viewer showing a foreach loop low-level C#

Let’s also look at a simple List initialization and what ReSharper’s IL Viewer reveals.

List<int> items = new () { 1, 2, 3, 4, 5};
Console.WriteLine(string.Join(",", items));
Lowering example in Visual Studio 2022 and ReSharper's IL Viewer showing a List low-level C#

Wow! .NET has lowered our single-line initialization of a List<int> into five separate calls to Add. While the lowered version is much simpler to read and comprehend, the version on the left is much nicer to type. 

Let’s try one more, and this time, let’s go wild with it!

using var httpClient = new HttpClient();
await httpClient.GetAsync("https://jetbrains.com/resharper");
Lowering example in Visual Studio 2022 and ReSharper's IL Viewer showing an httpclient with async/await in low-level C#

Those two lines of C# code use three high-level features of C#, and each gets lowered. Those features include the var keyword, async/await, and the using statement. In a top-level statement program, .NET produces a lowered output of 108 lines! While those lines of code may be difficult to read for us as humans, the compiler has an easier time taking the lowered-form output and turning it into IL.

Summary

Lowering happens to your C# code, whether you know about it or not. So while you could ignore it, using ReSharper’s IL Viewer can help you make more sense of newer C# language features. Likewise, viewing lowered versions of existing code can help you better understand it and spot potential bugs. As you saw above in the three examples, lowering can produce a wide range of outputs, so it’s fun to experiment and see what’s happening.

I recommend reading C# Lowering from community member Steven Giesel, and Lowering in the C# Compiler by Matt Warren.

If you have any fun lowering examples you’d like to share, please leave them in the comments below. As always, thanks for reading and being part of the JetBrains Community.

Image Credit: Simon Zhu (red metal tower)

image description

Discover more