Есть несколько причин. В первую очередь, вероятно, сделать его кроссплатформенным. Если C # или другие языки .NET скомпилированы непосредственно в нативный код, их придется перекомпилировать для каждой платформы, на которой они работают. В случае виртуальной машины весь код может храниться в промежуточном формате, и вам нужно только написать реализацию виртуальной машины для каждой платформы.
Кроме того, имея не зависящий от языка промежуточный язык, вы можете иметь много языков высокого уровня (C #, VB.NET, Python и т. Д.), Все ссылки на сборки написаны на других языках. Поскольку все они компилируются в одно и то же, они могут беспрепятственно работать друг с другом.
Есть и преимущества в производительности. JIT-компилятор может выполнять агрессивную оптимизацию специально для машины, на которой выполняется код в это время. Я не знаю, какую оптимизацию выполняет компилятор .NET JIT в этом смысле, но теоретические преимущества могут быть очень большими.