这个问题可以从Unity支持的.NET标准和实现细节两个角度看。
从对 .NET 标准支持的角度看,这只是一个版本的问题。
Unity的 .NET Runtime 版本是 2.0,支持的 .NET Profile 是3.5,默认的 C# 语言版本是 4.0。
这和你用 Visual Studio 2017 把语言版本降到 4.0,写一个 3.5 的库是一样的,区别只是在于版本比较落后,很多 .NET 4.x 的新功能不能用。
其他主流的 .NET 实现有传统的 .NET Framework ,跨平台移动开发用的Xamarin/Mono和跨平台的 .NET Core。
这三种实现相对比较统一,支持4.0的 Runtime,4.6 的库和 C# 7。自从去年加入了 .NET Foundation Technical Steering Group,Unity也在努力向最新标准靠拢,现在微软的Xamarin团队正在和Unity一起做这个事情。
这个过程中最重要的里程碑就是即将到来的 .NET Standard 2.0,其发布之后包括Unity在内的主流 .NET 实现都会支持这一标准,这样一来在 .NET 标准层面上Unity就真的和其他 .NET 实现没有区别了。
在实现细节上,Unity和其他 .NET 实现相比还是有短时间内难以弥补的差距。主要原因是Unity的技术架构比较复杂,又有很强的跨平台需求,所以升级起来比较困难。Unity Scripting的技术架构从下到上主要有三个部分:
1. C++ 编写的引擎内核,Unity 绝大部分核心功能都在这一层实现。
2. IL2CPP,一个 AOT 的runtime实现,提供 .NET 执行引擎和 GC等服务。Unity 从Mono 迁移到 IL2CPP的主要动力主要是相比于不停修改 Mono,基于 C++的工具链更容易将 runtime移植到新的平台上。
3. 基于 Mono嵌入技术的用户脚本。Unity相当于在一个C++写的native程序中内嵌了一个 Mono环境,包含引擎核心功能的 native API以UnityEngine.dll这样的形式封装起来提供给 Mono脚本调用。而脚本实际上被编译成一个普通的 .NET assembly,Unity加载以后根据需要调用和执行。
另外除了用户自己写的 C#脚本,Unity 编辑器的大部分 UI和 UnityEngine.UI.dll也是以用户脚本的形式在这一层实现的。
Unity 跟其他 .NET 实现的主要差距是在runtime性能和工具支持方面。首先是build pipeline,即使Unity支持了 .NET Standard 2.0,可能也没法短时间内把编译器换成Roslyn,意味着不能第一时间用上 C# 7。主要原因是Roslyn生成的debug符号跟mono差别比较大,debug 引擎需要做相应的修改。其次缺少对NuGet的支持也让Unity比较难融入 .NET 生态系统,Unity 的Project 系统和包管理系统都很薄弱,诡异的pipeline想要支持NuGet 需要很大的改动,微软方面也要配合才行。Unity 内部也才开始讨论这个问题,并没有明确的方案,能实现估计要相当长时间。
Runtime 方面也不乐观,首当其冲的就是 GC。Unity 想要换一个更好更适合 C#的 GC,难度主要在于复杂的执行环境,Unity 需要在 native环境下操纵托管环境下的脚本对象,想支持 GC分代回收和移动对象位置,这部分还是有很多工作需要做。然而如果不把 Boehm GC换掉,升级 .NET Profile的意义就变得很微妙,.NET和 C#的很多设计都基于对 GC性能的信心,比如async/await,没有一个好 GC,用起来还是会束手束脚。
也许很快我们就会有一个支持最新 .NET 标准的 Unity了,然而其在开发体验和性能方面还是会长时间落后于其他 .NET实现。希望这期间不要被别的引擎干掉就好。