.NET是什么?为什么要选择.NET?

自我们启动迭代迅猛的.NET 开源跨平台项目以来,.NET 发生了翻天覆地的变化。我们对整个平台进行了重新梳理与打磨,一方面新增大量面向性能与安全性的底层核心能力,另一方面配套推出侧重开发效率的高层特性,Span<T>、硬件内部函数、可空引用类型便是典型代表。
我们即将推出全新系列博客《.NET 设计要点》,深度剖析构成当下.NET 平台的底层原理与设计决策,同时讲解这些设计能为你当下编写的代码带来哪些增益。
本系列首篇博文将宏观概述.NET 平台的核心支柱与设计出发点。文中会从底层基础层面说明,选择.NET 能为开发者带来哪些核心能力;本文内容客观详实、侧重事实,你也可以用这套表述向他人介绍.NET 平台。
后续博文会针对上述各项主题展开深度讲解 —— 本篇篇幅有限,未能充分展开介绍各项特性。本文不涉及 Visual Studio 等开发工具,也不会讲解ASP.NET这类高层类库与应用开发模型。

后续文章:

在深入细节之前,有必要先聊聊.NET 的应用现状。全球数百万开发者都在使用它,在多种操作系统与芯片架构上开发云应用、客户端应用及各类其他程序。

 不少知名平台都依托.NET 运行,例如微软云 Azure、程序员社区 Stack Overflow 以及游戏引擎 Unity。
 各行各业、规模大小不一的企业普遍在使用.NET,其中大型企业的应用尤为广泛。在很多行业里,掌握.NET 技术是求职的一大加分项。

 

.NET 设计要点

.NET 平台四大核心价值:开发高效、性能强劲、安全可靠、运行稳定。.NET 在这四大价值之间实现的均衡取舍,正是其独特吸引力所在。

 

.NET 的核心设计思路可以概括为:无论在安全托管域(主打高效开发),还是非安全原生域(提供强大底层能力),都兼顾易用性与运行效率。

 .NET 或许是内置功能最丰富的托管运行环境,同时对外交互的互操作开销极低,二者无需做出取舍。实际上,大量特性正是依托这种无缝衔接的分层架构实现的:依托操作系统与 CPU 的原生底层能力,封装出安全易用的托管接口。

我们可以对这套核心设计思路展开细说:

  • 覆盖全技术栈,运行时、类库、编程语言与配套工具协同优化开发者使用体验。
  • 安全代码作为主流计算模型,非安全代码则用于实现额外的手动性能优化。
  • 同时支持静态代码与动态代码,可适配各类差异化开发场景。
  • 原生代码互操作、硬件内部函数开销极低,且高度保真(可直接调用原生 API 与 CPU 指令)。
  • 代码具备跨平台可移植性(支持不同操作系统、芯片架构),同时可针对特定平台做定制适配与深度优化。
  • 依托通用编程模型的专用实现方案,可适配云服务、客户端、游戏等各类开发领域。
  • 优先采用 OpenTelemetry、gRPC 等行业标准,而非自研专属方案。

 

.NET 技术栈的核心支柱

运行时(CLR, Common Language Runtime)、基础类库(FCL,Framework Class Library)与编程语言(C#/F#/VB.NET等)构成了 .NET 技术栈的根基。像.NET 命令行工具、ASP.NET Core 应用框架等上层组件,均搭建在这三大支柱之上。三者属于共生依存关系,由同一批开发者(微软员工与开源社区)协同设计、联合开发,开发人员往往同时参与多个组件的研发,并相互同步设计思路。

 

C# 是面向对象语言,运行时原生支持面向对象特性;C# 依赖垃圾回收机制,而运行时内置了跟踪式垃圾回收器。事实上,完整形态的 C# 无法移植到不具备垃圾回收能力的系统中。基础类库(以及各类应用框架)将底层能力封装为易懂的抽象概念与对象模型,让开发者能够以直观的流程高效编写业务逻辑。

C# 是一种现代、安全且通用的编程语言,从高级特性(如面向数据的record类型)到低级特性(如函数指针)都有涵盖。它提供了静态类型检查和类型及内存安全性的基本功能,这同时提高了开发人员的生产力和代码安全性。C# 编译器也是可扩展的,支持插件模型,使开发人员能够通过额外的诊断和编译时代码生成来增强系统。

众多 C# 语言特性与业界前沿编程语言相互借鉴、彼此影响。例如,C# 是第一个引入asyncawait异步语法的主流语言。同时,C# 借用了在其他编程语言中首次引入的概念,例如模式匹配、主构造函数等函数式编程特性。

核心类库提供数千种内置类型,其中大量类型与 C# 语言深度联动、互为支撑。比如 C# 的 foreach 可遍历任意集合,搭配基于模式的优化机制,让 List<T> 这类集合能够简洁且高效地遍历处理。资源释放可交由垃圾回收自动处理,也能通过 IDisposable 接口配合语言原生 using 语法实现资源即时清理。

C# 字符串插内插语法兼具可读性与高性能,依托 stringStringBuilderSpan<T> 等核心库底层类型实现;语言集成查询(LINQ)则基于类库中数百个序列处理方法(Where、Select、GroupBy 等)实现,整体设计可扩展,同时支持内存数据与远程数据源查询。

类似的深度融合案例不胜枚举,语言内置特性仅仅是 .NET 核心类库能力的冰山一角:从压缩、加密到正则表达式,类库均提供完整实现。网络功能自成一套完备体系,底层覆盖套接字到 HTTP/3 协议;除此之外,类库还支持 JSON、XML、tar 等海量文件格式解析处理。

.NET 运行时最初被称为“公共语言运行时 (CLR)”。它继续支持多种语言,有些由微软维护(例如 C#、F#、Visual Basic、C++/CLI 和 PowerShell),有些则由其他组织维护(例如 Cobol、JavaPHP、Python、Scheme)。大量运行时优化与语言无关,所有托管语言都能同步受益。

下文我们将介绍三大支柱协同打造的各项平台核心特性。我们固然可以分开详述每个组件,但你很快就能发现:三者紧密协作,共同落地 .NET 的核心设计理念。我们先从类型系统开始讲起。

 

类型系统

.NET 的类型系统覆盖能力十分全面,在类型安全、表达能力、动态特性与原生互操作四大维度做到均衡兼顾。
首要一点,这套类型系统完整支撑面向对象编程范式:包含各类基础类型、单继承机制、支持默认方法实现的接口,以及虚方法分派,为面向对象体系下所有层级类型提供规范、合理的运行行为。
泛型是贯穿整个平台的核心特性,支持基于一种或多种类型对类做特化。举个例子,List<T> 是开放泛型类;实例化后的 List<string>List<int> 无需像早年 ArrayList 那样,单独编写 ListOfStringListOfInt 专用容器,也不用依赖 object 基类配合强制类型转换。借助泛型还能跨不同数据类型构建通用工具体系(大幅减少重复代码),泛型数学库便是典型应用。
委托与 Lambda 表达式支持将方法作为数据传递,可轻松在第三方系统的执行流程中嵌入自定义逻辑。二者相当于 “粘合代码”,且签名通常支持泛型,适用场景非常广泛。
app.MapGet("/Product/{id}", async (int id) =>
{
    if (await IsProductIdValid(id))
    {
        return await GetProductDetails(id);
    }

    return Products.InvalidProduct;
});
Lambda 的这种用法是 ASP.NET Core 极简接口(Minimal APIs)的核心特性,开发者可直接将接口处理逻辑传给路由系统。
在新版 ASP.NET Core 中,框架对整套类型系统的运用变得更加深入广泛。
和受垃圾回收托管的引用类型不同,值类型以及栈分配内存块,让开发者能更直接地操控底层数据、对接原生平台。.NET 中绝大多数基础原始类型(如各类整型)都属于值类型,开发者也可以自定义具备相同行为特性的值类型。
.NET 泛型体系完整兼容值类型:像 List<T> 这类泛型容器存储值类型时,内存布局为连续平铺结构,不存在额外内存损耗。除此之外,当泛型参数替换为值类型时,.NET 会生成专属编译代码,这条泛型执行路径能够规避高昂的垃圾回收开销。
byte magicSequence = 0b1000_0001;
Span<byte> data = stackalloc byte[128];
DuplicateSequence(data[0..4], magicSequence);
这段代码会生成栈上分配的值。Span<byte> 是安全且功能更完善的 byte*(字节指针)替代方案,自带长度信息(配套边界越界检查),还提供便捷的切片截取能力。
引用相关类型与引用变量构成一套轻量化底层抽象模型,用于操作类型系统中的数据,Span<T> 便属于此类。这套模型并非通用编程模型,存在诸多严格限制,目的是保障内存安全。
internal readonly ref T _reference;

通过 ref 这种写法,只会复制指向底层存储的指针,而非复制指针所引用的完整数据。值类型默认采用「值拷贝」机制,而 ref 能实现「引用拷贝」,可大幅提升程序性能。

 

自动内存管理

.NET 运行时依靠垃圾回收器(GC)实现自动内存管理。对任意编程语言而言,内存管理模型几乎是其最具辨识度的核心特性,各类 .NET 语言也同样如此。
堆损坏类 bug 向来极难调试,开发人员往往要耗费数周、甚至数月才能定位问题。许多语言选择垃圾回收机制,以此从根源消除这类故障:GC 能精准管控对象生命周期。 常规垃圾回收会批量释放内存以保证运行效率,但过程会产生程序停顿。如果业务对延迟要求极其严苛,这类停顿会难以接受,同时 GC 程序本身内存占用也会更高。不过垃圾回收通常具备更优的内存局部性,部分 GC 还支持堆压缩,能有效降低内存碎片产生概率。
.NET 搭载自调优跟踪式垃圾回收器。绝大多数场景下无需人工干预即可稳定运行,同时针对极端业务负载开放丰富配置项。这套 GC 历经多年持续迭代优化,适配过海量不同类型业务场景。

指针 bump 分配机制

分配对象时,仅将分配指针按对象所需内存大小向后偏移(无需在零散空闲块中寻找可用空间),同一批创建的对象会在内存中连续存放。
由于这些对象通常会被一同访问,大幅提升内存局部性,对程序性能至关重要。

分代回收

对象生命周期普遍符合分代假说:绝大多数对象要么生命周期极短、很快销毁,要么长期存活。
因此 GC 绝大多数时候只需回收短期临时对象(临时代回收),无需每次都扫描完整堆(完整堆回收),回收效率大幅提升。

堆压缩

同等容量的空闲内存,整合为少量大块空间远比分割成大量细碎小块更有利用价值。
压缩式 GC 执行时,会将存活对象移动至连续区域,从而整合出大片空闲内存。该机制实现难度高于不移动对象的 GC,因为需要同步更新所有指向被移动对象的引用。
.NET GC 会动态判断收益,仅当压缩带来的内存收益高于回收开销时才执行压缩;日常临时代回收大多会开启压缩。

并行回收

GC 工作可单线程或多线程执行:
工作站模式(Workstation GC)采用单线程执行回收;服务器模式(Server GC)启用多条专用 GC 线程,回收速度大幅提升。 
服务器 GC 会创建多个独立堆供程序分配内存,支持更高内存分配速率,非常适合追求吞吐的业务。

并发回收

传统回收需要暂停所有业务线程(STW,全局停顿),实现逻辑简单,但停顿时长有时无法容忍。.NET 提供并发 GC 模式,以此缓解停顿问题。

固定(Pinning)

.NET GC 支持对象固定功能,实现托管代码与原生代码间零拷贝互操作。该能力支撑高性能、高保真的原生交互,给 GC 带来的额外开销极低。

可替换独立 GC

可通过配置文件指定自定义独立 GC 实现,只需满足标准接口规范即可替换原生 GC。方便开发者调试分析、实验全新内存回收方案。

完备诊断能力

GC 输出结构化、丰富的内存与回收日志,可与系统其他监控数据关联分析。
举例:抓取 GC 事件并与 IO 等其他事件联动,量化 GC 延迟在整体尾部延迟中的占比,区分性能瓶颈来源,便于精准优化对应模块。

 

安全性

编程安全是过去十年中的热门话题之一。同时也是 .NET 这类托管运行环境与生俱来的底层设计能力。

安全体系分为三类

  • 类型安全 — 一个任意类型不能替代另一个类型,避免未定义行为。
  • 内存安全 — 仅使用分配的内存,例如变量要么引用一个活动对象,要么是 null
  • 并发或线程安全性 — 共享数据不能以会导致未定义行为的方式被访问。

补充说明: 美国联邦政府最近发布了关于内存安全重要性的指导

.NET 在立项之初就将安全作为核心设计目标。它最初的定位是支撑新一代 Web 服务,而 Web 服务天然需要在互联网这种风险最高的计算环境中处理不可信外部输入。如今行业已形成共识:Web 程序应当采用具备内存安全特性的语言开发。

类型安全由编程语言与运行时协同强制校验。编译器负责静态约束校验,例如将字符串赋值给流对象这类类型不匹配操作,会直接报编译错误;

运行时负责动态约束校验,例如跨不兼容类型强制转换,运行时会抛出 InvalidCastException。

内存安全主要依靠即时编译器(JIT)与垃圾回收器配合实现:变量只会指向存活对象、空值或已脱离作用域;内存默认自动初始化,新创建对象不会读取未初始化脏内存。数组边界校验机制可拦截非法下标访问(这类错误大多源于差一错误),不会放任程序读取未知内存,而是抛出 IndexOutOfRangeException。

空值处理是内存安全的细分分支。可空引用类型 是 C# 语言与编译器配套特性,能在编译期静态识别未妥善处理空值的代码:

若你对可能为 null 的变量做解引用操作,编译器会主动给出警告;你也可以限制变量禁止赋值 null,一旦将可能为空的值赋给该变量,编译器同样会告警。运行时配套动态校验逻辑,一旦访问空引用会立即抛出 NullReferenceException 阻断危险操作。

该特性依赖类库中配套的可空特性注解;同时基础类库、各类应用框架都全面完整标注了这套注解,才能让静态分析工具为业务代码给出精准可靠的空安全检测结果。

string? SomeMethod() => null;
string value = SomeMethod() ?? "default string";
C# 编译器会判定这段代码具备空安全特性,因为代码中明确声明并妥善处理了空值,其中 ?? 空合并运算符起到关键作用。变量 value 永远不会为空,与其声明的类型约束保持一致。
.NET 并未内置开箱即用的并发安全保障。开发者需要遵循既定的编码范式与规范,以此规避各类未定义行为。同时 .NET 生态提供各类代码分析器与配套工具,可辅助定位并发相关隐患。核心类库中也提供大量支持并发场景的类型与方法,例如并发集合,它支持任意数量的读写线程同时操作,不会出现数据结构损坏问题。
运行时提供安全代码与非安全代码两套代码模型。默认使用的安全代码可保证完备安全性;若要使用非安全代码,需要开发者显式开启。非安全代码一般用于对接底层平台、硬件交互,或是对性能关键路径做手动底层优化。
沙箱是一种特殊的安全机制,用于实现组件隔离、限制组件间的资源访问权限。.NET 依托行业标准隔离技术实现隔离能力,例如进程(含 CGroups)、虚拟机、WebAssembly,各类隔离方案具备各自不同的特性。

 

错误处理

异常是 .NET 中主要的错误处理模型。异常的优势在于:无需将错误信息体现在方法签名中,也不必让每一层方法都处理错误。

下方代码展示了一种典型使用范式:

try
{
    var lines = await File.ReadAllLinesAsync(file);
    Console.WriteLine($"The {file} has {lines.Length} lines.");
}
catch (Exception e) when (e is FileNotFoundException or DirectoryNotFoundException)
{
    Console.WriteLine($"{file} doesn't exist.");
}

规范的异常处理对应用稳定性至关重要,可预期的异常需要在业务代码中主动捕获处理,否则程序会直接崩溃。相比出现未知未定义行为的程序,直接崩溃的程序更便于定位问题、排查故障。
异常会在发生错误的位置抛出,并自动收集程序运行时的各类诊断信息,这些信息可用于交互式调试、应用可观测性监控以及事后崩溃分析。上述每一种排查手段,都依赖详尽的错误信息与程序现场状态来定位故障根因。
异常机制设计用于处理罕见异常场景,一部分原因是异常抛出的性能开销相对较高。即便偶尔有人用异常做流程分支控制,这也并非其设计初衷。

异常机制也承担取消任务的能力:一旦检测到取消请求,可快速终止当前执行流程,并逐层释放调用栈中正在执行的任务。

try 
{ 
    await source.CopyToAsync(destination, cancellationToken); 
} 
catch (OperationCanceledException) 
{ 
    Console.WriteLine("Operation was canceled"); 
}
.NET 提供多种设计模式作为替代错误处理方案,专门应对抛出异常带来的性能损耗无法接受的场景。
 
例如 int.TryParse 会返回布尔值,解析成功时通过输出参数存放转换后的合法整型数值;Dictionary<TKey, TValue>.TryGetValue 采用同类设计,查找命中时通过输出参数返回对应的有效值。
错误处理以及更宽泛的诊断能力,由底层运行时 API、高层类库与配套工具协同实现。这套能力体系针对容器等新型部署方案做了适配设计。
 
典型示例:dotnet-monitor 内置面向诊断的 Web 服务,可将应用运行时数据向外推送至监控接收端。

 

并发性

几乎所有业务场景都离不开多任务并行执行能力:客户端程序在后台运算的同时保持界面流畅响应;服务端承载成千上万并发请求;设备同时响应多路外部信号;高性能机器并行处理密集型计算任务。操作系统依靠线程实现并发,多条指令流可独立调度执行,操作系统负责将线程分配到设备空闲处理器核心上运行。系统同时配套 IO 处理机制,支持大规模并发 IO,任意时刻可存在大量正在执行中的 IO 请求。编程语言与框架会在这套底层能力之上封装多层抽象。
.NET 通过基础类库与深度集成到 C# 语言的特性,提供多层级并发与并行处理能力。
  • Thread 是最底层抽象,直接封装操作系统线程,开发者可手动创建线程并等待其执行完毕。
  • 线程池(ThreadPool)构建在线程之上,开发者只需关注待执行任务,任务会异步调度至池内线程;线程的扩容、回收、任务分发全部交由运行时自动管理。
  • Task 统一抽象所有异步操作,支持多种创建与等待方式:Task.Run 将委托调度至线程池执行并返回任务对象标识执行结果;Socket.ReceiveAsync 返回 Task<int>(或 ValueTask<int>),代表从套接字读取数据的异步 IO 操作。
.NET 提供丰富同步原语,用于在线程与异步任务之间协调同步 / 异步流程;同时封装大量高层 API 简化常见并发模式开发,例如 Parallel.ForEachParallel.ForEachAsync 可便捷并行遍历处理数据集。
异步编程是 C# 的顶级特性,依托 asyncawait 关键字,开发者能够轻松编写、组合异步逻辑,同时完整复用语言原生各类流程控制语法。

 

反射

反射是一种「程序即数据」编程范式,程序的某一部分代码能够基于程序集、类型、成员信息,动态查询并调用另一部分代码。它对晚期绑定编程模型与各类开发工具尤为实用。
下方代码通过反射实现类型查找与调用。
foreach (Type type in typeof(Program).Assembly.DefinedTypes)
{
    if (type.IsAssignableTo(typeof(IStory)) &&
        !type.IsInterface)
    {
        IStory? story = (IStory?)Activator.CreateInstance(type);
        if (story is not null)
        {
            var text = story.TellMeAStory();
            Console.WriteLine(text);
        }
    }
}

interface IStory
{
    string TellMeAStory();
}

class BedTimeStore : IStory
{
    public string TellMeAStory() => "Once upon a time, there was an orphan learning magic ...";
}

class HorrorStory : IStory
{
    public string TellMeAStory() => "On a dark and stormy night, I heard a strange voice in the cellar ...";
}
这段代码会动态枚举程序集中所有实现指定接口的类型,逐个创建类型实例,并通过该接口调用实例上的方法。其实这段逻辑完全可以用静态代码实现 —— 因为它查询的只是当前项目已引用程序集内的类型;但静态写法需要外部传入待处理实例集合,比如 List<IStory>。如果这段逻辑需要从插件目录加载任意未知程序集,就更适合采用这种晚期绑定方案。当程序集与类型在编译阶段无法预知时,反射是这类场景的常用方案。
反射可以说是 .NET 提供的动态能力最强的一套机制。设计初衷是允许开发者自定义二进制加载器与方法分发器,其运行语义既可以和运行时定义的静态代码规则保持一致,也可以做出差异化实现。反射对外暴露一套完整丰富的对象模型,简单场景上手门槛很低;但随着业务场景复杂化,开发者需要深入理解整套 .NET 类型体系才能驾驭。
反射还支持另一种高级模式:在运行时生成 IL 中间字节码并交由 JIT 即时编译,常用于将通用算法替换为针对性优化的专用实现。序列化工具、ORM 对象关系映射框架在解析完对象结构等信息后,常会采用这套机制提升性能。

 

编译的二进制格式

应用程序与类库会编译为基于 PE/COFF 规范、跨平台统一标准的字节码。以二进制文件分发代码首先是一项面向性能的设计,它能够支撑项目规模持续扩张、容纳数量庞大的工程。
 
每个类库内部都附带一份记录导入、导出类型的元数据库,这份元数据在开发流程与程序运行阶段都承担关键作用。
编译后的二进制文件包含两大核心组成部分:
  1. 二进制字节码(IL)
     
    格式精简规整,经 C# 这类高级语言编译器编译后,程序运行时无需再解析文本源码。
  2. 元数据(Metadata)
     
    记录所有导入、导出类型信息,同时标注每个方法对应的字节码存储位置。

在开发阶段,各类开发工具可高效读取元数据,快速获取类库对外暴露的全部类型、以及哪些类型实现了指定接口等信息。该机制大幅提升编译速度,同时让集成开发环境(IDE)等工具能够精准展示当前上下文下可用的类型与成员列表。

对于运行时,依托元数据,类库支持延迟加载,方法体更是可以做到按需延迟解析。前文介绍的反射,就是运行时访问元数据与 IL 字节码的标准 API;除此之外还有更适配开发工具的专用接口。

IL 字节码格式长期保持向下兼容。最新版 .NET 依然可以加载并执行由 .NET Framework 1.0 编译器生成的二进制程序集。
共享类库一般通过 NuGet 包分发。默认情况下,单个二进制文件就能适配所有操作系统与芯片架构;同时 NuGet 包也支持多平台专用二进制,在不同环境中提供差异化逻辑实现。

 

代码生成

.NET 中间字节码无法直接交由硬件执行,必须通过代码生成器转换为可执行指令。主流实现分为四种:提前编译(AOT)、即时编译(JIT)、解释执行、转译编译,目前各类业务场景均有落地使用。
.NET 最为人熟知的是即时编译(JIT)。JIT 在程序运行期间、方法首次被调用时才将其编译为原生机器码,“即时” 之名由此而来。 举例:某类型包含多个方法,但程序运行时只会调用其中一部分。同时 JIT 能利用仅运行时才能获取的信息做深度优化,例如静态只读变量的初始化值、当前设备的 CPU 型号;同一方法可多次重编译,基于前几次运行采集的特征数据,针对不同目标持续调优。
JIT 会生成适配指定操作系统与芯片架构的机器码。.NET 内置多套 JIT 实现,支持 x64、Arm64 等指令集,兼容 Windows、macOS、Linux 系统。 .NET 开发者无需关心各类 CPU 指令集、系统调用约定的差异,全部由 JIT 生成符合 CPU 规范的指令;JIT 会针对不同硬件生成高性能代码,同时操作系统与芯片厂商也会协同参与优化适配。
提前编译(AOT)逻辑与 JIT 相近,区别在于机器码在程序运行前一次性生成。开发者选用 AOT 的核心原因是省去运行时 JIT 编译开销,大幅缩短程序启动耗时。AOT 编译产物天生绑定特定操作系统与硬件架构,若要多环境分发,需要额外打包多版本程序。例如同时支持 Windows/Linux、x64/Arm64,就需要构建四套独立安装包覆盖全部组合。AOT 也能实现不少优化,但整体优化上限通常不及 JIT。
解释执行与转译编译将在后续文章介绍,二者在 .NET 生态中同样承担关键作用。
代码生成器的核心优化手段之一是硬件内建函数(Intrinsics)。硬件内建函数可将 .NET API 直接映射为 CPU 原生指令,该方案已在 .NET 类库中大规模用于 SIMD 向量并行指令优化。

 

互操作

.NET 在设计之初就明确以低开销原生互操作为核心目标。.NET 应用与类库能够无缝调用底层操作系统 API,或是接入庞大的 C/C++ 原生类库生态。
新版 .NET 运行时持续完善底层互操作基础能力:支持通过函数指针调用原生方法、将托管方法暴露为非托管回调函数、自定义接口转换逻辑等。该领域功能仍在迭代升级,.NET 7 推出源码生成式互操作方案,进一步降低调用开销,同时完美兼容 AOT 编译场景。
下方示例展示了 C# 函数指针搭配 .NET 7 新增的 LibraryImport 源码生成器带来的高效互操作能力(这套源码生成能力是在 .NET 初代就已存在的 DllImport 基础上做的增强封装)。
// Using a function pointer avoids a delegate allocation.
// Equivalent to `void (*fptr)(int) = &RegisterCallback;` in C
delegate* unmanaged<int, void> fptr = &RegisterCallback;
RegisterCallback(fptr);

[UnmanagedCallersOnly]
static void Callback(int a) => Console.WriteLine($"Callback:  {a}");

[LibraryImport("...", EntryPoint = "RegisterCallback")]
static partial void RegisterCallback(delegate* unmanaged<int, void> fptr);
第三方独立包依托上述底层互操作基础组件,封装出面向特定业务领域的高层互操作方案,典型代表包括 ClangSharp、Xamarin.iOS、Xamarin.Mac、CsWinRT、CsWin32 以及 DNNE。
上述全新特性,并不代表运行时内置的互操作能力(例如托管 / 非托管数据封送、Windows COM 互操作)不再具备使用价值 —— 我们清楚大量开发者长期依赖这些能力,因此运行时会持续兼容这些传统内置功能。 但这类原生内置互操作仅作向下兼容之用,官方不再计划对其迭代升级。后续所有研发资源,都会投入到底层互操作基础组件,以及基于组件构建的各类领域专用互操作方案。

 

二进制分发

微软 .NET 团队维护多套官方二进制发行包,近年来新增对安卓、iOS、WebAssembly 平台的支持。团队采用多种技术手段,针对各类环境对代码库做定制适配。平台绝大部分代码使用 C# 编写,因此跨平台移植工作仅需聚焦少量底层核心组件。
社区维护另一套发行体系,主要面向各类 Linux 发行版。例如 Alpine Linux、Fedora、红帽企业 Linux、Ubuntu 系统均预装 .NET。
社区厂商也将 .NET 移植到更多异构平台:三星完成基于 Arm 架构的 Tizen 系统适配;红帽与 IBM 将 .NET 移植到 LinuxONE/s390x 大型机;龙芯中科完成龙架构(LoongArch)移植。我们期待更多合作伙伴将 .NET 适配至更多软硬件环境。
Unity 科技已启动长期规划,对其内置的 .NET 运行时进行现代化升级改造。
.NET 开源项目的架构与维护模式,支持个人、企业及各类机构以标准上游开源协作模式共同开发。微软作为平台托管方,负责项目治理与基础设施(如持续集成流水线)建设。微软团队会与各类机构协作,协助其顺利使用或移植 .NET。项目拥有宽松的上游合入策略,可接纳仅适用于特定发行版的定制修改。
源码构建(source-build)是核心工作方向,Canonical(Ubuntu 厂商)等多家机构依靠该项目,遵循 Linux 发行版标准流程编译 .NET。近期配套推出虚拟单一代码仓(VMR)进一步完善该体系:.NET 原本拆分大量独立代码仓,虽然方便日常开发,但完整编译整套产品流程繁琐;VMR 统一整合所有仓库源码,解决完整打包构建的难题。

 

总结

现代化 .NET 已经迭代多个版本,近期我们发布了 .NET 7。从 .NET Core 1.0 至今,我们一直在平台底层持续打磨,因此我们认为有必要对这套底层设计目标做一次完整梳理。我们延续了初代 .NET 的核心设计理念,但最终打造出了一条全新技术路线,为开发者带来更多、更丰富的价值。
我们回到开篇的核心主旨收尾。.NET 始终围绕四大核心价值打造:开发效率、性能、安全、稳定性。我们坚信,各类编程语言平台应当各有所长、提供差异化的技术方案,才能更好地服务开发者。作为研发团队,我们致力于让 .NET 开发者拥有极高的开发效率,同时打造一套在性能、安全、稳定性方面具备领先水准的平台。
本系列后续还会推出更多文章。大家希望我们优先讲解哪些主题?欢迎在评论区留言。是否希望看到更多这种宏观全景式的深度内容?
如果喜欢这类底层原理文章,可以阅读《公共语言运行时(CLR)简介》

这篇文章由 Jan KotasRich LanderMaoni Stephens和 Stephen Toub撰写,感谢 .NET 团队各位同事提供思路并参与审稿。

 

本文翻译自:https://devblogs.microsoft.com/dotnet/why-dotnet/

原文更新于:2023年2月14日

非常推荐阅读《CLR via C#(第4版)》这本书,书中对.NET的技术底座进行了详细介绍。

美中不足的是这本书是基于.NET Framework 4.5.2进行讲解的,对于今天的.NET Core平台来说,确实会存一些内容滞后,但是该书底层 CLR 基础原理依然通用,只是缺少新版.NET的一些功能特性而已。

posted @ 2026-06-18 10:06  zhaotianff  阅读(9)  评论(0)    收藏  举报