---
title: "What is the difference between C#, .NET, IL and JIT?"
description: "When you get started or even if you have quite some knowledge it can be confusing to juggle with those terms.\nSo what is the difference between **C#** and **.NET**? And what does it have to do with **IL** and **JIT**?"
updated: 2022-06-14
tags: ["C#", ".NET", "MSIL", "IL", "JIT"]
canonical: "https://steven-giesel.com/blogPost/aa23a1c9-8ab2-4b05-9bd2-52624af7b684/what-is-the-difference-between-c-net-il-and-jit"
---

# What is the difference between C#, .NET, IL and JIT?

There is a lot to unpack and obviously, those terms are not the same. So let's see what is the difference between **C#**, **.NET**, <abbr title="Intermediate Language">**IL**</abbr> and the <abbr title="Just in Time">**JIT-Compiler**</abbr>. To do so we start with a nice overview where we go from top to bottom to explain the differences.

![overview](https://linkdotnetblog.azureedge.net/blog/20220612_CSharpDotNetJit/Dotnet.webp)

## C# / F# / ...
Let's begin with the part on the top: The **languages** themselves. Most prominent we have **C#** and we have for example **F#**. Both are just languages. That is all! It is the same as let's say *English*. A language is defined by a set of symbols (the alphabet) and a set of rules that generates the language (grammar). The more scientific explanation of automata theory is:
> In automata theory, a formal language is a set of strings of symbols drawn from a finite alphabet. A formal language can be specified either by a set of rules (such as regular expressions or context-free grammar) that generates the language, or by a formal machine that accepts (recognizes) the language.

<sup>[Source](https://www.sciencedirect.com/topics/computer-science/formal-language)</sup>

As English gives you some nice words and grammar it does not automatically give you any sentences. You have to create those sentences. And multiple sentences can carry meaning. The same applies to C#, F# and friends. These languages just give you the rules (keywords and grammar) but you have to build something with it.

That brings us to the next step, because that alone is not enough. The next step in the chain is a **compiler**. Your C# compiler takes your source code and generates an assembly. But what is in that assembly? Well still your instructions you wrote in your C# code but in a different format: the **Intermediate Language** sometimes also referred as **Common Intermediate Language**. The same does a F# compiler. It takes your F# code and emits IL instructions into an assembly.

## **Intermediate Language**
Before I go into detail I amazing thing before: Because all of the .NET languages emit the same intermediate language, you can use an assembly that was written originally in C# in your F# code and vice versa. That is why it is called **common** intermediate language.
As the term might suggest it is not the end of the line. But we have two major gains:
 * We are closer to the underlying OS/hardware and all the languages emit that exact language. From now on we don't have to deal anymore with different stuff.
 * That said it lays groundwork for further optimizations later on.

Not everything a language offers will end up in the **IL** code. Here is a small example:
```csharp
public class MyClass
{
    public readonly string ReadonlyText = "some text";
    public string Text = "some text";
}
```

The `readonly` qualifier (which is a C# language feature) tells the compiler to check whether a user changed a reference to the object. But guess what, the C# compiler does not emit any `readonly` IL code. `readonly` is purely used for static analysis and is neither used in **IL** or in the **JIT** phase. Let's have a look at the [generate IL code](https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEBLANgHwAEBmAAkICZSBZATwGFcBDAZxYFgAoAby9P/JlYTACYQAdrlrkAjAAZSAJRiiJUgCowEGUgF5SAIhYQAtjFIYtGAwG4+AkrIWbtew8bMWrtrgF8gA):
```csharp
IL_0000: ldarg.0
IL_0001: ldstr "some text"
IL_0006: stfld string MyClass::ReadonlyText

IL_000b: ldarg.0
IL_000c: ldstr "some text"
IL_0011: stfld string MyClass::Text
```

It is absolutely identical. I read from time to time that `readonly` can improve performance. And the easy answer is: **No** it can not, because the IL code is the same. Another way to see this is to use reflection. You can reassign the reference of `readonly` fields at runtime without any problem.

Back to the **CIL**. If you want you could also write **CIL** code directly removing the need for A C# or F# compiler. [Here](https://stackoverflow.com/questions/12741607/how-to-compile-cil-code) is small exert how to do so :D.

To wrap this up: A compiler in the .NET world takes your code and emits "new" code in the form of the intermediate language. Different compiler emit different IL code. For example the F# compiler, in contrast to the C# compiler, can emit [`tail.`](https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.tailcall?view=net-6.0) instructions. `tail.` is used for tail-call optimization, which if you are not familiar with, I covered [here](https://steven-giesel.com/blogPost/ccdbefd9-2875-49e6-929c-c5081d5b4d27). If you want to see this in action [here](https://sharplab.io/#v2:DYLgZgzgNALiCWwoBMQGoA+wCmMAEYA9oXgIZ4DGhAdvgLyU34AU5aeAjAJRA===) you go.

All the stuff we looked at happen at compile time (besides you writing code ... that is called design time) so we will now go into the realm of the **runtime** and the **JIT**-compiler.

## JIT
As I said earlier the **Intermediate Language** is just one step but not the end of the line. And that is when we first have to talk about the **.NET runtime** before we can come to what the **JIT**-compiler does.

## .NET runtime (CLR)
The idea behind .NET CLR (common language runtime) is to write code once but run it anywhere but the intermediate language is just not enough. Someone has to translate those IL instructions into something the operating system can handle. And that is what the .NET runtime does. When you click on an executable assembly (exe) the machine knows to invoke the .NET runtime which than utilizes the JIT (more later). But wait a second how can we write independent code and on double-click the OS knows what to do? Short: It does not. Long: That is the magic of your C#/F# compiler. If you produce an executable assembly (aka exe) it also emits OS- and architecture-dependent code how to start the CLR, which then takes care of IL and stuff. For libraries of course that does not apply.
So we can see the CLR has to be written for all platforms and architectures where our code should run.

### JIT
Back to the JIT. The **JIT** is used by the **CLR** to emit native machine code instructions which will run on that specific operating system and architecture. There is a difference if you have Windows x86 vs x64. 

The JIT does not only translate IL to native code but also puts it into memory so the next time the CLR can directly run the full-native code. Another job of the JIT is to optimize code even further. The C# compiler can only statically do that but the JIT has to the full picture and can do stuff the IL can't. For example **loop unrolling**. 
```csharp
for (int i = 0; i < 1024; i++)
    Foo(i);
```

Will become:
```csharp
for (int i = 0; i < 1024; i += 4)
{
    Foo(i);
    Foo(i + 1);
    Foo(i + 2);
    Foo(i + 3);
}
```

The advantage of loop unrolling is that we can have less branch prediction misses (more info [here](https://steven-giesel.com/blogPost/0c84f371-08e3-4a57-bdd7-8b08cb308232)), reducing the amount of machine commands and possible even execute the commands in parallel thanks to **SIMD**. There are not many ways to influence the JIT from C# code itself. There are things like `MethodImplOptions.AggressiveInling` which tells the JIT that he should try to inline that the given function, but you can't force him to do so.

That brings us last but not least to **.NET**.

## What is .NET then?
What we understand under **.NET** is basically all of the above said and more. If you are using functions like `Console.WriteLine` that is of course also part of .NET. To be very specific such stuff is part of the **BCL** - **B**ase **C**lass **L**ibrary. But also the C#/F#,... compiler is part of .NET as well as runtime components, debugger, command line tools and so on.

## Conclusion

I hope I could give you a good overview over the differences between C#,F#, .NET and the underlying IL code and the JIT-compiler.