02-01-历史-CSharp诞生记-从Cool-CLR到标准化与平台共演 📅 2026/8/20 13:06:57 C# 诞生记从 Cool、CLR 到标准化的语言与平台共演系列C# 与常用数据结构源码剖析 · 历史与演化篇阅读时间约 55 分钟阅读方法本文将可由发布物和标准确认的事实、参与者的事后回忆、以及作者的技术解读分开。不使用未能核对原始录音或文本的“名人引语”不将团队工程简化为单一天才的决定。一、“Anders 为什么创造 C#”不是一个可单句回答的问题C# 的初始语言设计由 Anders Hejlsberg 领导这是官方资料、标准编辑记录和多年公开访谈都能支持的事实。但这不意味着 C# 只是他一个人“灵光一现”的产物。一门可投入产品的语言需要语法与类型系统设计、编译器、运行时、基础类库、调试器、IDE、文档、测试、标准化与客户反馈的协同。更准确的问题是20 世纪 90 年代末Microsoft 为什么需要一个新的托管平台和与之紧密配合的现代 C 风格语言Hejlsberg 和同事带入了哪些经验语言、CLR、CLI 和基础类库如何相互约束对此可以给出受证据支持的工程解释Microsoft 需要统一当时分散的 Windows 应用开发体验提供类型安全、自动内存管理、跨语言组件和可版本化元数据的执行环境同时需要一门让 C/C 家族开发者容易入门又能直接表达组件、属性、事件和元数据的语言。C# 是这个平台项目的重要部分而不是一门脱离运行时独立设计后再“移植”上去的语言。二、诞生前的工具与语言背景Hejlsberg 在加入 Microsoft 前已经长期从事 Pascal 家族编译器和开发工具工作。Turbo Pascal 将编辑、编译和运行放在紧凑的工作流中Delphi 将 Object Pascal、可视化组件、属性编辑器、事件处理和原生 Windows 应用结合起来。这段经历是解释 C# 的重要背景。但不应把 C# 的每个特性都画成“Delphi 直接迁移”。属性、事件和组件开发的经验显然有连续性但 C# 的类型系统、IL 目标、元数据、验证、异常与跨语言互操都是在 CLR/CLI 语境下重新定义的。Microsoft 当时也有 Visual Basic、Visual C、COM 与 ActiveX 等多条技术路线。COM 可以实现二进制组件互操但引用计数、接口描述、注册、错误传播和版本兼容需要大量约定。C 提供了控制力和原生性能也将内存安全、头文件、模板实例化和 ABI 复杂度交给开发者。新平台的目标并非证明这些技术“失败”而是为新一代网络与 Windows 应用提供一致的托管基础。三、Java 和 Visual J 的背景有影响但不是全部因果Java 在 1990 年代中期展示了一组当时很有吸引力的选择类似 C 的表面语法、字节码虚拟机、类加载、类型安全与垃圾回收。Microsoft 提供 Visual J 和自己的 Java 工具/类库Hejlsberg 加入 Microsoft 后也参与了相关工作。Windows Foundation ClassesWFC是这段历史中的重要库与工具经验。Sun 与 Microsoft 围绕 Java 兼容性与授权发生过法律争议。这是可查证的时代背景但将 C# 简化成“诉讼后迫不得已创造的 Java 替代品”会过度压缩多年平台研发。新运行时、多语言元数据、基础类库和组件模型的范围都超出了“换一门 Java 语法”。同样把 C# 称为“抄袭 Java”或反过来否认 Java 的影响都不是有效的技术史方法。C#、Java、C、Object Pascal、Smalltalk 等语言共享更早的语法和对象模型传统又对类型、组件、运行时和工具做出不同权衡。可以逐项对比设计但不应用外观相似替代年代、文档和实现证据。四、Cool可确认的代号与不宜写死的起点Microsoft 内部早期语言项目使用过Cool代号通常被解释为与 C 风格、面向对象语言有关的缩写。早期预览材料、参与者回忆和后来的官方历史记述能支持 Cool 是 C# 的早期名称。但“项目在 1998 年某天正式开始”这类精确叙述往往来自事后回忆而语言原型、运行时项目、类库与组织命名未必在同一天跨过清晰边界。如果没有原始项目记录更谨慎的写法是“20 世纪 90 年代末开始设计”再从 2000 年的公开发布进入硬时间线。Cool 到 C# 的改名也有多个后来流传的故事包括商标、乐理中 sharp 的隐喻以及#像四个加号的视觉双关。其中某些可由参与者讲述支持但不应将有趣的命名民间说法当成语言设计的技术因果。五、语言与 CLR 共同设计C# 早期设计的关键不是“编译到某种中间码”这一件事而是语言能够与 Common Language Runtime 的类型系统、元数据、异常、GC、安全检查和跨语言调用一起演进。这使 C# 中很多表面语法有明确运行时表示。例如class实例是受 GC 跟踪的引用类型struct是 CLI 类型系统中的值类型可嵌入对象、数组或栈上活动记录但不能简化为“struct 永远在栈上”。装箱则是值类型进入对象/接口世界时的明确转换。属性不是“有两个特殊命名的普通方法”那么简单CLI 元数据可以描述属性及其访问器工具、组件设计器与反射 API 能以一致方式发现它。事件同样有元数据表示和 add/remove 访问器契约可以约束外部代码只能订阅和退订不能代替或在声明类外任意触发事件。委托则在类型安全签名下表示可调用目标是事件、回调和后来 Lambda 表达式的运行时基础之一。它不是语法层面消灭所有间接调用成本的“零开销函数指针”而是将方法引用纳入类型系统和托管对象模型。六、可核验的时间线时间可核验事件解读边界1983Turbo Pascal 产品发布可说明 Hejlsberg 的编译器/工具背景不能单独证明 C# 某特性因果1995Delphi 发布属性、事件和组件工具经验的重要前史1996 前后Hejlsberg 加入 Microsoft个人入职不等于 C# 项目在当天开始1990 年代末Cool 和新托管平台的早期设计精确起始月份需原始项目档案宜保持宽时间窗2000Microsoft 公开 C# 与 .NET 平台计划/预览从内部代号进入公开产品史的稳固节点2001 年 12 月ECMA-334 与 ECMA-335 第一版发布C# 语言与 CLI 分别进入 ECMA 标准后续 ISO/IEC 版次是另一时间节点2002.NET Framework 1.0 和 Visual Studio .NET 2002 发布首个正式产品平台不等于语言历史的起点2005C# 2.0 / .NET Framework 2.0 时代的泛型产品化说明首版并非已拥有后来全部设计时间线中最容易混淆的是“宣布”、“ECMA 标准发布”、“ISO/IEC 采用”和“产品发布”。C# 在 2000 年已公开ECMA-334/335 第一版标注为 2001 年 12 月对应 ISO/IEC 标准随后在 2003 年发布.NET Framework 1.0 则在 2002 年成为正式产品。如果只写“C# 2002 年诞生”仍会把内部设计、公开预览、标准化和产品发布压成一点。正式引用应使用 ECMA 官方历史版页面或第一版 PDF 的版次与月份不把 ECMA 和 ISO 日期混为一谈。七、C#、.NET Framework、CLR 与 CLI 不是同一个名字C#是语言其规范描述源程序的词法、语法、类型、转换、表达式和声明等语义。CLR是 Microsoft .NET Framework 的 Common Language Runtime 实现负责加载、执行、JIT、GC、异常与运行时服务。.NET Framework是包含 CLR、基础类库与应用框架的 Windows 产品。CLICommon Language Infrastructure是 ECMA-335 标准化的基础设施规范包含 Common Type SystemCTS、Common Language SpecificationCLS、元数据、组件格式与 Virtual Execution System 等内容。CLI 不等于 CLR 全部产品行为就像语言标准不等于 Visual Studio 的所有工具功能。ECMA-334 规范 C#ECMA-335 规范 CLI。二者分开很重要可以有其他语言面向 CLI也可以有不同实现执行 C# 编译后的 CLI 组件。早期 .NET 的多语言叙事正是建立在共享类型系统、元数据和基础类库上而不是强迫所有开发者使用同一源语言。八、标准化为什么重要将 C# 和 CLI 提交给 ECMA 标准化使核心规范成为可公开阅读、评论和独立实现的文本。后来 ISO/IEC 也发布对应标准。这不意味着所有 Microsoft 库、Visual Studio 特性和 Windows 框架都成为标准也不意味着每份标准都与最新产品同步。标准文本最有价值的地方是它能将“某个编译器恰好这样做”与“符合规范的程序必须这样解释”分开。读者研究 C# 1.0 时应阅读对应版本规范而不是用今天编译器的最新语义倒推。研究 CLI 时也要固定标准版本因为泛型等能力在后续版本发生重要扩展。九、C# 1.0 的类型安全与受控的底层能力C# 与 CLR 的一个核心方向是把大部分应用代码放在托管、类型安全环境中。对象引用的类型、数组访问、转换、方法签名和元数据可由编译器与运行时检查GC 追踪托管对象的可达性减少手动释放导致的悬挂指针和重复释放。这不代表 C# 程序不会有资源泄漏、竞态条件、无限保活、索引错误或与原生代码互操风险。GC 管理托管内存的生命周期不会自动关闭文件、释放操作系统句柄或解决业务对象之间的逻辑引用。C# 也保留unsafe与指针等受控逃生口用于原生互操和必要的底层编程。这显示其目标不是从语言中删除所有不安全能力而是让普通代码默认处于可验证的托管区域将越界操作显式标记并由项目配置允许。十、属性、事件与委托的组件意义属性允许组件暴露“像状态一样使用”的 API同时保留访问器中的验证、计算和版本演化空间。但属性语法不保证访问廉价、无副作用或线程安全API 设计仍需遵守明确约定。委托使回调签名进入类型系统事件在此基础上将发布者与订阅者的权限分开。对 UI 和组件开发这比仅靠命名约定或通用“监听器对象”更容易被语言工具、反射和设计器识别。它们也与 Delphi 时代的组件开发经验存在明显联系。但这些特性并非由 Hejlsberg 一人完成语言、CLI 元数据、类库委托类、编译器降级和 IDE 设计。从历史上准确地说他的经验和设计领导影响了方向而产品能力是多团队协作的结果。十一、为什么 C# 1.0 没有泛型C# 1.0 的数据结构生态受一个重要限制语言和当时 CLI 产品没有今天的泛型。通用集合主要通过object存储元素例如早期ArrayList和Hashtable。这意味着调用者在取回元素时需要转换值类型可能发生装箱类型不匹配会延迟到运行时暴露。// C# 1.x 时代常见的非泛型集合使用形状。 ArrayList values new ArrayList(); values.Add(42); // int 装箱为 object int number (int)values[0]; // 调用方显式转换泛型不是单独向 C# 语法添加尖括号就能完成的特性。它需要 CLI 元数据表示泛型参数、约束和构造类型需要加载器、JIT、反射、调试器和类库一起扩展。C# 2.0 与 .NET 2.0 时代将泛型带入产品ListT、DictionaryTKey,TValue、IEnumerableT等才构成今天 C# 数据结构生态的主干。这也是阅读历史时要防止“现在主义”的例子。今天看似理所当然的泛型集合并不存在于 C# 1.0/.NET Framework 1.0 的原始组合中。将后来能力写回诞生时刻会掩盖语言与运行时协同演化的真正难度。十二、泛型如何改变数据结构生态泛型集合把元素类型变成 API 契约的一部分。Listint只能接受int错误类型在编译时被拒绝值类型可以在泛型数组中直接存储不需要因为进入object容器而逐项装箱。IEqualityComparerT和IComparerT使哈希与排序策略在类型安全签名下复用。但不能将 C#/.NET 泛型历史简化为“Java 使用擦除C# 使用 reified所以 C# 完全没有代价”。CLI 保留构造泛型类型信息支持值类型实例化与反射运行时仍可以对引用类型共享部分机器码并需处理代码尺寸、约束调用和 AOT 等工程权衡。数据结构的进化也不只由语言版本决定。BCL API 设计、CLR 泛型实现、JIT 专用化、GC 与数组布局、线程内存模型共同影响ListT和DictionaryTKey,TValue的行为。这是本专栏要把 C# 语法、BCL 源码和运行时分层阅读的原因。十三、版本兼容从诞生之初就是平台问题平台一旦承载组件和应用语言演化就不能只考虑新代码是否优美。旧二进制是否能在新运行时加载类库更改是否破坏方法签名元数据如何描述组件身份不同语言生成的类型能否互操都是平台契约。CLI 元数据和组件身份为版本化提供基础但不会自动解决所有兼容性。给公共类增加成员、修改虚方法、改变序列化形状、替换异常或调整泛型约束都可能影响源代码或二进制兼容。后来 .NET 的强命名、组件加载、类库版本与统一策略也都经历过迭代和取舍。这种约束也解释了为什么 C# 会持续添加特性却很少直接重新定义旧程序的核心含义。不能由此声称 C# 绝对不会有破坏性变化更准确的说法是兼容性是语言和平台演化的主要设计约束之一具体版本仍要核对 breaking change 文档和实际构建。十四、团队而不是单一英雄Hejlsberg 作为首席设计者对 C# 的语言方向具有关键影响他在 Turbo Pascal、Delphi 和 Microsoft 开发工具中的经验也是了解语言取舍的有效线索。但 C# 规范、编译器和 CLR 还有众多设计者、工程师、测试者、文档作者、标准委员会成员和早期用户的工作。一些历史文章会列出若干核心团队成员这有助于打破单人神话但名单也很难穷尽多年工作。如果要对某项特性做精确归因应寻找当时的设计备忘录、规范提案、会议记录、代码提交和具名访谈而不是仅因为某人是项目最知名人物就将一切归给他。领导者需要统一目标、权衡需求并让语言、运行时与工具长期保持方向。准确的叙事是“Hejlsberg 领导了团队与平台项目的语言设计”而不是“他独自创造了全部 C#”。十五、不要猜测设计者动机历史技术文章很容易把一项特性解释成设计者的个人偏好例如“因为他不喜欢 Java 的某点所以加入了某功能”。除非有同时期设计文档或可核对的当事人说明否则这只是一种事后心理推断。更稳妥的写法是陈述问题和结果非泛型集合需要转换和装箱C# 2.0/CLI 泛型给出了某种解法事件发布需要类型安全回调与订阅权限委托和 event 提供了语言/元数据模型。至于团队中每个人的内心动机只在有可引用证据时记录。本文也不使用那些在互联网上重复出现、却找不到原始访谈页码或录像时间戳的引语。如果一句话对论证很重要应给出原始来源、日期、完整上下文和讲话者如果做不到用自己的话对可验证设计做概括不要用引号制造权威。十六、与 C、Java 和 Delphi 的联系应怎样写背景对理解 C# 有帮助的联系不应跨过的结论C/C花括号语法家族、类/结构体、运算符、受控 unsafe 能力C# 不是 C 的语法子集或二进制兼容后继JavaC 风格托管语言的同时代背景类型安全、GC、虚拟执行的相似问题相似语法不能单独证明“抄袭”也不能否认影响Delphi/Object PascalHejlsberg 的编译器、RAD、属性/事件和组件工具经验C# 的所有特性并非 Delphi 换一套语法Visual Basic/COMWindows 生产力、组件元数据和跨语言互操的现实需求CLR/CLI 不是 COM 的简单重命名最有价值的比较单位是“同一问题的不同解法”。例如 C 模板、Java 早期非泛型集合与后来的泛型、CLI 泛型各自如何处理代码复用和类型信息COM 接口元数据与 CLI 元数据如何支持跨语言组件。这种对比可以落到规范和实现。十七、反事实要保持谨慎反事实问题例如“如果没有 Sun 诉讼C# 还会出现吗”“如果 Hejlsberg 没有加入 Microsoft.NET 会不会只有 Visual Basic”这些问题有启发性但历史没有可重运行的对照组。我们可以指出约束Microsoft 已在研发新托管平台Windows 开发确实有统一运行时和类库需求Hejlsberg 确实带有编译器和组件工具经验Java 争议确实影响了 Microsoft 的产品环境。但无法从这些事实唯一推导出另一条历史时间线。因此反事实只宜用来暴露我们对因果的假设不宜作为结论。本文能说的是“哪些条件同时存在产品最后如何形成”而不是“只要去掉其中一个人或一场诉讼结果就必然怎样”。十八、来源层级什么证据能支持什么结论第一层同时期原始资料。包括 ECMA/ISO 标准对应版本、Microsoft 早期 SDK/白皮书/发布新闻、当年的 PDC 材料、原始编译器与类库产物。它们最适合确认当时公开了什么、规范了什么和产品实际包含什么。第二层可定位的参与者记录。包括完整讲座、访谈、设计备忘录和会议记录。它们适合说明当事人如何回忆目标与取舍但应保留记忆偏差、事后概括和访谈情境的边界。第三层高质量二手历史。它们应列出原始来源区分时间线和解释并显示争议。二手资料有助于组织跨数年的材料但不应只因为被多个网站转载就升格为原始证据。第四层无来源转述与趣闻。包括无页码的引语、无日期的“内部故事”和仅靠命名双关推导的动机。它们可以作为待核查线索不应承担技术史论证。十九、读者验证清单阅读任何一篇“C# 诞生史”时可以逐项检查文章是否区分 Cool 内部设计、2000 年公开、2001 年 12 月 ECMA 第一版、2002 年产品发布和后续 ISO/IEC 版次关于项目起点的精确年月是来自原始档案还是参与者多年后的回忆是否将 C#、CLR、CLI、.NET Framework 和 Visual Studio 当成同一事物引语是否有讲话者、标题、日期、页码或录像时间戳文章是否将语言、运行时和工具的多团队工作都归功于一个人对 Java、C 或 Delphi 的联系是逐项技术比较还是使用“抄袭/超越”口号是否将 C# 2.0 泛型、C# 3.0 LINQ 或更晚特性写成 C# 1.0 已有能力对诉讼、入职或某次会议的因果是否有设计文档支持还是只是好讲的故事是否把“标准化”误写成全部 .NET 产品开源或所有类库均属标准技术结论能否回到对应版本的 C# 规范、CLI 标准和产品类库验证结语C# 是一次协同设计不是一个孤立语法集C# 的诞生可以由一条较稳固的线索理解Hejlsberg 将编译器、RAD 和组件开发经验带入 MicrosoftJava、C、Visual Basic、Delphi 和 COM 构成了当时技术背景Microsoft 的新托管平台需要类型安全、GC、元数据、跨语言组件和现代工具链C# 与 CLR 在团队协作中共同演进然后分别通过语言规范、CLI 规范和 .NET Framework 产品进入公开世界。属性、事件与委托体现了组件开发的重要方向值类型、托管引用和 unsafe 边界体现了安全与底层能力的权衡。但首版并没有泛型今天数据结构生态中最熟悉的ListT和DictionaryTKey,TValue依赖后来 C#、CLI、CLR 和 BCL 的同步扩展。因此“为什么创造 C#”最好的答案不是一句无法核对的引语也不是一场诉讼或一位设计者的单一动机。它是一组可观察的工程需求和历史条件最终在一门语言、一个通用运行时基础、一套类库和工具中同时落地。读取这段历史时像阅读源码一样固定版本、区分契约与实现、为每个强结论寻找原始证据才能看到 C# 真正的设计价值。下一篇C# 版本演化全景上1.0 到 4.0