The CLR & Intermediate Language
Run `dotnet build` on a one-line method and you get a `.dll`. Open it and the C# is gone, replaced by something that is neither your source nor your processor's machine code, a third language in between.
- ▸Your C# isn't what runs: it becomes a stack-based instruction set first, and native code only at run time.
- ▸The shipped
.dlldescribes itself: names and signatures sit in tables beside the code, so tools can reconstruct the source. - ▸A method call on a null reference throws at the call site, because the language null-checks nearly every instance call.
Take one method and build it:
// User.cs public int AddPoints(int a, int b) => a + b;
$ dotnet build $ ls bin/Debug/net8.0/ User.dll User.pdb User.deps.json User.runtimeconfig.json
User.dll is the build output. Open it in a viewer and the C# is gone. What sits inside is not C# source, and it is not your processor's machine code either. It is a third representation: a compact, processor-independent instruction set that Roslyn (the C# compiler) emits in place of your source.
That is the first surprise. Building does not produce something a CPU can run directly. It produces an in-between form. A second translation, the one that actually yields machine code, happens later, when the program runs, not when you build it.
So two translations stack between the C# you type and the instructions a CPU executes. The first is Roslyn turning source into the in-between form, at build time, once. The second turns that form into native code for whatever processor is present, at run time, every time the app starts on a new machine. The same User.dll therefore runs unchanged on an x64 server and an Arm64 laptop: the build artifact is processor-neutral, and the machine-specific step is deferred until there is a machine to specialize for.