Because that's how it works for very every single interpreted and bytecode compiled language?
And the thing that changed in the early 2000s was a massive shift toward using interpreted and bytecode compiled languages.
If we're specifically talking .NET, the thing that changed since the early 2000s is that creating a self-contained executable became possible in the first place. On Windows, .NET *.exe files were still run by an outside runtime, it's just that, since Microsoft owned the whole platform, it was easy for them to hide all that behind a curtain, ensure .NET is preinstalled with Windows, etc. The design constraints changed when .NET became cross-platform. OS X and Linux require a bit more (or at least different) finagling in order to achieve good user experience.