Elytra
Blog2026

.NET 的平台弯路:闭源锁 Windows,开源来得偏晚

CLI 早就能跨平台,微软却把 .NET 锁在 Windows。闭源守城、被 Java 占先、Mono 先行开路——2014 年才转向开源跨平台,产品路线绕了二十年。

随笔
GPT 5.6+1
csharpjavadotnetclrjvmhistoryrider

对比 C# 和 Java,一个绕不过去的问题是:

C# 明明更好用——structref/out、properties、真正的泛型、async/awaitSpan<T>……为什么后端世界还是 Java 的?

这个直觉里有一部分是对的——C# 在很多语言设计上长期比 Java 激进、完整。但要把 语言设计平台战略 分开看。

真正影响格局的,往往不只是 structasync/await,还有微软一度把 .NET 当成 Windows 护城河——闭源、守 Windows,在传统企业后端让 Java 先占了位,到 2014 年才正式转向开源跨平台。技术上很早就走得通,产品路线却绕了二十年。

核心结论

这里有个容易混的概念:CLI/CLR 是标准和技术底座,.NET Framework 是微软当年推出的具体产品。 前者早就能跨平台,后者却被做成 Windows 专属——故事的关键在这个落差里。

  • 标准走得通,产品走窄了:2000 年代微软把主力运行时锁在 Windows 上,开源也来得偏晚,传统企业后端让 Java 占了先手。
  • Java 赢在部署,不只在语法:语言相对保守,但 JVM 跨平台 + 企业生态在传统市场非常稳固。
  • 社区先趟路,官方后跟上:Rotor、Mono 先后证明可行,官方实现晚了十几年——中间那些年,开发者没少折腾。
  • 2014 年后方向拨正:总比一直不开源要好;云、游戏、AI 等新场景里仍有机会——只是前面二十年耽搁的时间,换不回来。

C# 是怎么诞生的?

C# 大约从 1999 年前后开始在微软内部设计,最初代号是 Cool(C-like Object Oriented Language),主要设计者是 Anders Hejlsberg、Scott Wiltamuth、Peter Golde。2000 年 7 月随 .NET Framework 计划正式公开。

Hejlsberg 此前做过 Turbo Pascal、Delphi,也参与过微软 Visual J++。所以 C# 的血统实际上是:

Pascal/Delphi 的开发效率
  + C/C++ 的语法习惯
  + Java 的 GC/VM 思路
  + 微软自己的组件模型需求

微软官方的 C# 规格也明确列出了早期目标:现代、通用、面向对象,同时特别强调 component-oriented programming、统一类型系统、版本演进、类型安全

把 C# 简化成「微软抄 Java」并不准确。它确实明显受 Java 影响,但从第一天就在解决一些 Java 刻意没有解决的问题。

C# 思路Java 长期的对应情况
struct / value type没有用户定义真正 value type
propertiesgetFoo() / setFoo()
delegates / events后来靠接口、匿名类、lambda 等
ref / out没有直接对应物
operator overload基本禁止
reified generics泛型 type erasure
attributesannotations 后来加入
nullable value types后来有 Optional,但不是同一回事
LINQStream API 晚很多
async/await没有语言级完全对应模型
Span<T> / ref struct一直较难表达
unsafe / native interop相对克制

Hejlsberg 的设计哲学一直比较像:只要可以在保持类型安全的情况下给程序员更多表达能力,就尽量提供。

那为什么当年不跨平台?

这里是整个故事最有意思的地方。

CLI 本来就是可以跨平台的,但 .NET Framework 这个产品被微软做成了 Windows 平台。

这两个东西不要混在一起。

微软在 2000 年就把 C# 和 CLI 提交给 ECMA 标准化,2001 年完成标准;甚至 2002 年还发布过 Rotor / Shared Source CLI,它真的能运行在 Windows XP 和 FreeBSD 上。

也就是说:技术上,它从很早就证明可以跨平台。

问题在于商业版 .NET Framework 里面塞进了大量 Windows 基础设施:

Win32 → COM/COM+ → Windows Forms → ASP.NET/IIS
  → WPF → Windows Registry → Windows security

微软今天也把老 .NET Framework 描述成 Windows-only 的实现。

早期的商业逻辑其实是:

Windows 是微软最重要的产品
  → .NET 是 Windows 的开发平台
  → C# 是 .NET 最好的语言
  → 开发者喜欢 C# → 写 .NET → 软件依赖 Windows
  → Windows 生态更牢固

从 2000 年微软的商业环境看,这个战略并不荒唐。但长期来看,确实付出了不小的代价——主要是时间和生态上的。

Java 为什么把服务器抢走了?

Java 比 C# 早了整整五年

Java 1995 年公开;它从设计阶段就把「异构机器和操作系统」作为核心问题。Oracle 今天的 JVM 规格仍然直接写着:JVM 是 Java 平台实现硬件和操作系统独立性的基石。

于是到 C# 2000 年出现时,Java 已经开始形成网络效应:

Java 企业栈 circa 2000 Linux / Solaris / Unix JVM Servlet / JSP / App Server Oracle / IBM / BEA / Sun 银行 / 电信 / 政府 / 大企业

而 C# 给企业的答案却是:

.NET 企业栈 circa 2000 Windows Server IIS .NET Framework ASP.NET SQL Server

在纯微软企业里非常舒服。但如果企业已经有 Oracle + Linux + Solaris + IBM + Unix,Java 显然风险更小。

所以 Java 赢服务器,并不是 Java 语言比 C# 高级,而是 JVM 的部署战略比 .NET Framework 正确。

微软后来「后悔」了吗?从 Windows-only 到开源跨平台

微软从未公开说过「后悔没早点跨平台」。但 2014 年之后的动作,几乎就是在修正早年的路线。

在传统企业后端,Java 的先发优势确实很难撼动——银行、电信、政府那套栈一旦跑通,迁移成本极高。等 .NET Core 真正可用、够稳、够完整时,那一波窗口多半已经过去。这不等于 .NET 没未来——开源跨平台总归是对的,云原生、AI 工具链里也还能长出新机会——但前面二十年耽搁掉的开发和生态成本,是实打实的

2014 年微软宣布开源完整的服务器端 .NET 栈,并让 .NET 运行在 Linux 和 macOS。官方解释为什么开源 .NET Core 时甚至非常直接,给出了两个主要理由:

  1. 建立跨平台 .NET 的基础
  2. 建立更强大的生态系统

微软还亲自承认了一个非常尴尬的问题:因为官方实现不开源,Mono 社区不得不重新实现一遍 .NET,导致两个代码库重复工作、行为不一致。最合理的方法就是:一个共同的、开源的、跨平台代码库。

Mono 其实早在 2001 年就开始干这件事了——2001 年 8 月 Mono CLR 就已经能运行程序,生成的 C# 程序可以在 Linux 上运行。

这个历史特别讽刺:

社区花十几年证明「.NET 完全可以跨平台」,然后微软自己终于把官方 .NET 做成了跨平台。

Rotor 的教训

Rotor 2002 年有一百多万行源码,但许可证主要用于研究、教学、调试、实验——不是 OSI 意义上的自由开源。微软 2014 年后来承认:Rotor 的许可证实际上无法成为跨平台 .NET 生态的基础。

这是典型的:「源码可见」 ≠ 「开放生态」。

为什么一开始连开源都不愿意?

你得用 2000 年微软的世界观理解,而不是 2026 年微软。

那时候微软的核心资产是:

Windows + Office + Visual Studio + SQL Server + Windows Server

微软当时对开源的总体战略态度和今天差别极大。他们不是完全不给你源码——Rotor 就是例子——但许可证明确限制了你能否在此基础上建立生态。

.NET 绕了哪些弯路?

如果把二十多年放在一起,主线其实很清晰:语言层面的 C# 没拖后腿;平台层面却绕了二十年弯路,代价由整个生态分担。

Windows lock-in 太久

这是影响最大的一点。如果 2002 年的官方 CLR 就以跨平台、开源的姿态推出来,服务器语言的格局、游戏引擎的运行时选择,今天很可能都不一样。

开源来得偏晚

这直接导致了 Mono 这种「社区重新实现微软标准」的局面——两边各维护一套,开发者跟着折腾,兼容性时不时出问题。

平台长期碎片化

历史上出现:

.NET Framework
.NET Compact Framework
Silverlight
Windows Phone
Mono
Xamarin
.NET Core

然后又 .NET 5 → 6 → 7 → 8 → ...。今天统一之后已经舒服很多,但这个统一花了将近二十年。

语言侧:C# 没拖后腿

顺带一提,C# 的语言设计有很多地方确实非常超前——这也是为什么开篇那个直觉会产生。写性能敏感代码时特别容易体会到:

struct Vector3
{
    public float X;
    public float Y;
    public float Z;
}

你可以得到连续布局:

XYZ XYZ XYZ XYZ XYZ

而传统 Java 对:

class Vector3 {
    float x, y, z;
}

典型语义更接近:

ref → object
ref → object
ref → object

这对 cache locality、GC pressure、SIMD、游戏 ECS、图形、物理当然很重要。

纠正一个常见误解

Java 并不是「所有东西都是 new + heap」。Java 有 intfloatdouble 等 primitive。而且即使写 Foo x = new Foo(),HotSpot 也会做 Escape Analysis 和 Scalar Replacement——JIT 证明对象不需要真的存在时,可以把字段变成寄存器/局部值,从而消除 allocation。

但你的核心感觉仍然没错:C# 把 value semantics 暴露成了语言和 CLR 类型系统中的一等概念;Java 长期主要依赖 JVM 优化器帮你猜。

OpenJDK 的 Project Valhalla 干了十几年,本质上就是在补这一块:把 value classes / value objects 加进 Java/JVM。某种意义上可以说:

Java 花了二十多年重新走向 C# 早期 value type 思路的一部分。

当然 Valhalla 的设计比简单复制 C# struct 要复杂得多,因为它必须兼容几十年的 JVM 二进制生态。

泛型也是典型例子。Java 的 List<Integer> 通过 type erasure 实现,运行时很多泛型类型信息不存在。C# 的 List<int> 在 CLR 里知道 int 是真正的 value type,可以得到 int int int int 的连续数据,而不是 Integer* 的指针数组。对游戏、数值计算、ECS,这个差距尤其明显。

Java 为什么还这么成功?是劣币驱逐良币吗?

我不太认同。

如果只比较语言表达能力和系统编程能力,现代 C# 的确比传统 Java 丰富很多。但 Java 成功的东西不是只有 Java language。

真正厉害的是:

JVM + portability + backward compatibility + enterprise ecosystem + tooling + library ecosystem

Java 最大的优势之一恰恰是它的保守。Java 官方语言规格甚至明确说,它故意避免采用大量未经验证的新语言特性,目标是保持简单、生产可用。几十年来你会不断看到这种哲学:

宁可难看一点,也不要轻易破坏旧代码。

于是你今天可以拿出非常古老的 .jar,很多情况下照样运行。银行特别喜欢这个。大型企业也特别喜欢。

C# 则更像:

「这个抽象真的好用,那我们把它加入语言。」

所以才一路出现 properties、delegates、events、generics、LINQ、extension methods、async/await、tuples、pattern matching、records、Span<T>、ref struct、source generators……

这是两个完全不同的文化。不是优劣,是 trade-off

Rider:JVM 写的 C# IDE?

这里还有一个很有趣的事实。Rider 实际上是:

┌─────────────────────────┐
│ IntelliJ Platform       │
│ Java / Kotlin / JVM     │
│ UI、Editor、VCS、IDE框架 │
└────────────┬────────────┘
             │ IPC
┌────────────▼────────────┐
│ ReSharper backend       │
│ .NET                    │
│ C# semantic analysis    │
│ refactoring             │
│ inspections             │
└─────────────────────────┘

JetBrains 官方自己就是这么描述的:Rider frontend 是运行在 JVM 上的 IntelliJ Platform,而 C# 的语言功能运行在独立的 ReSharper/.NET 后端进程,两者通过协议通信。

所以很有趣:

你在一个 JVM IDE 里,使用一个 .NET 后端分析 .NET 代码。

IntelliJ / Rider 内存大,Java 有没有锅?

有一点,但不能全怪 Java。

Java 缺乏成熟的用户自定义 value types,确实意味着大型 Java 程序很容易产生大量小对象。IDE 又恰恰是一种超级容易制造「小对象海洋」的软件:AST node、PSI node、symbol、reference、file、token、inspection、index entry……几百万乃至几千万个长期对象都不是不可想象的。

因此 object header、pointer、GC metadata、cache locality 都会形成成本。如果 JVM 原生 value objects 普及,这类 workload 理论上会受益很大。

不过 IntelliJ 吃内存还有一个更大的原因:

它故意拿内存换智能功能。

RustRover 官方把后台的 Project Analysis 描述成建立整个项目的 types、methods、objects 等虚拟映射,以支持 completion、inspection、refactoring、navigation、find usages。

所以:

VS Code ≈ 编辑器 + rust-analyzer / clangd / tsserver

而:

RustRover / IntelliJ ≈ IDE 自己建立巨大的项目语义数据库

这两种架构的重量不是一个量级。

为什么 RustRover lint 比 VS Code 慢?

Rust 是一个非常折磨 IDE 的语言:generics、traits、associated types、macros、proc macros、type inference、cargo feature graph。RustRover 还要维护 IntelliJ 自己那一整套 PSI / indexes / inspections。

而 VS Code + rust-analyzer 的路径非常专一:

VS Code → LSP → rust-analyzer → 专门优化 Rust incremental analysis

因此某些「我刚敲完这一行,多久出现红线」的场景,rust-analyzer 完全可能比 RustRover 感觉更灵敏。

这不能简单归结成「Java 慢」。JVM 本身可以非常快,HotSpot 是几十年优化出来的工业级 JIT。更准确的是:

IntelliJ Platform 很重 + JVM object model 有一定成本 + JetBrains 做的分析更多 + Rust 本身语义分析昂贵。

四个因素叠加。

压缩成一句话

如果把 C# 和 Java 的历史压缩成一句话:

Java 是一个语言设计相对保守、但平台战略异常成功的系统;C# 是一个语言设计异常优秀、但早期平台战略把自己限制在 Windows 的系统。

2000~2014 很像:

Java
  语言:      ★★★☆
  运行时:    ★★★★★
  跨平台:    ★★★★★
  生态:      ★★★★★

C#
  语言:      ★★★★★
  运行时:    ★★★★★
  跨平台:    ★★
  生态范围:  ★★★

到了现代 .NET,早年最吃亏的那块短板,基本已经补上了:

C#

Roslyn

IL

CoreCLR / RyuJIT

Windows / Linux / macOS

而且 Runtime、GC、JIT、BCL、Roslyn、ASP.NET Core 基本都已经公开开发。

收束

今天再看 .NET,会有一种感觉:微软终于把 CLR 设计之初就具备的可能性,认认真真做完整了。

这也正好解释了 Unity 与 CoreCLR 的关系——如果游戏引擎真正全面拥抱现代 CoreCLR/.NET,它接入的不只是「换了个更快的 Mono」,而是一套统一、开源、跨平台的官方底座。

回头看,最让人唏嘘的是明明技术上早就能走另一条路,产品路线却绕了二十年——闭源、碎片化、Mono 与官方实现各干各的。C# 作为语言其实做得相当好,市场声量却始终差一点火候;很大程度上是平台战略的账单,不是语法不够好。

好在 2014 年之后方向拨正了。总比一直不开源要好——Runtime、Roslyn、ASP.NET Core 今天都相当能打。前面耽搁的时间换不回来,但后面的路,其实还长。

On this page