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

自从我们启动快速发展的.NET 开源和跨平台项目以来,.NET 发生了很大变化。我们重新思考和完善了该平台,添加了专为性能和安全性而设计的新低级功能,并与以生产力为中心的更高级别功能相结合。Span<T>硬件内在函数可为 null 的引用类型都是示例。我们即将推出新的“.NET 设计点”博客系列,以探索定义当今 .NET 平台的基础知识和设计选择,以及它们如何使您现在编写的代码受益。

该系列的第一篇文章提供了平台的支柱和设计点的广泛概述。它从基础层面描述了当您选择 .NET 时“您将获得什么”,并且旨在成为一个充分且以事实为中心的框架,您可以使用它向其他人描述该平台。后续的帖子将更详细地讨论这些相同的主题,因为这篇文章并没有完全公正地描述这些功能。本文不描述 Visual Studio 等工具,也不涵盖 ASP.NET 提供的更高级别的库和应用程序模型。

跟进帖子:

在详细讨论之前,值得先讨论一下 .NET 的使用。它被数以百万计的开发人员用来在多个操作系统和芯片架构上创建云、客户端和其他应用程序。它还在一些知名的地方运行,例如AzureStackOverflowUnity。各种规模的公司(尤其是大型公司)都在使用 .NET,这一点很常见。在很多地方,懂技术就可以找到工作。

.NET设计点

.NET 平台代表生产力性能安全性可靠性。.NET 在这些价值观之间取得的平衡使其具有吸引力。

.NET 设计点可以归结为在安全域(一切都高效)和不安全域(存在巨大的功能)中保持有效和高效。.NET 可能是具有最多内置功能的托管环境,同时还提供最低的与外界互操作的成本,两者之间没有任何权衡。事实上,许多功能都利用了这种无缝划分,在底层操作系统和CPU的原始能力和功能上构建安全的托管 API 。

我们可以进一步扩展设计点:

  • 生产力是全栈的,包括运行时、库、语言和工具,所有这些都有助于开发人员的用户体验。
  • 安全代码是主要的计算模型,而不安全代码则支持额外的手动优化。
  • 静态和动态代码均受支持,可实现广泛的不同场景。
  • 本机代码互操作和硬件内在函数成本低且保真度高(原始 API 和指令访问)。
  • 代码可以跨平台(操作系统、芯片架构)移植,而平台目标则可以实现专业化和优化。
  • 通过通用编程模型的专门实现,可以实现跨编程领域(云、客户端、游戏)的适应性。
  • OpenTelemetrygRPC等行业标准比定制解决方案更受青睐。

.NET 堆栈的支柱

运行时、库和语言是 .NET 堆栈的支柱。更高级别的组件(例如 .NET 工具和应用程序堆栈(例如 ASP.NET Core))构建在这些支柱之上。这些支柱具有共生关系,由一个团队(Microsoft 员工和开源社区)共同设计和构建,其中个人负责多个组件的工作并为其提供信息。

C# 是面向对象的,运行时支持面向对象。C# 需要垃圾收集,并且运行时提供跟踪垃圾收集器。事实上,如果没有垃圾回收,就不可能将 C#(以其完整形式)移植到系统中。这些库(以及应用程序堆栈)将这些功能塑造成概念和对象模型,使开发人员能够在直观的工作流程中高效地编写算法。

C# 是一种现代、安全、通用的编程语言,涵盖从面向数据的记录等高级功能到函数指针等低级功能。它提供静态类型以及类型和内存安全作为基准功能,同时提高开发人员的工作效率和代码安全性。C# 编译器也是可扩展的,支持插件模型,使开发人员能够通过额外的诊断和编译时代码生成来增强系统。

许多 C# 功能已经影响了最先进的编程语言,或者受到了最先进编程语言的影响。例如,C# 是第一个引入async和await. 同时,C# 借用了其他编程语言中首次引入的概念,例如采用模式匹配主构造函数等函数式方法。

核心库公开了数千种类型,其中许多类型与 C# 语言集成并为 C# 语言提供支持。例如,C#foreach允许枚举任意集合,并通过基于模式的优化使集合能够List<T>简单高效地处理。资源管理可能会由垃圾收集来完成,但可以IDisposable通过using.

C# 中的字符串插值既富有表现力又高效,与 、 和 等核心库类型的实现集成并由其string提供StringBuilder支持Span<T>。语言集成查询 (LINQ)功能由库中的数百个序列处理例程(如WhereSelect和 )提供支持GroupBy,并具有支持内存和远程数据源的可扩展设计和实现。这样的例子不胜枚举,直接集成到该语言中的内容只是触及了作为核心 .NET 库的一部分公开的功能的表面,从压缩加密再到正则表达式。全面的网络堆栈有其自己的领域,涵盖从套接字HTTP/3。同样,这些库支持处理多种格式和语言,例如JSONXMLtar

.NET 运行时最初被称为“公共语言运行时 (CLR)”。它继续支持多种语言,一些由 Microsoft 维护(例如 C#、F#、Visual Basic、C++/CLI 和 PowerShell),一些由其他组织维护(例如 Cobol、JavaPHPPythonScheme)。许多改进与语言无关,这让所有人都感到不安。

接下来,我们将了解它们一起提供的各种平台特性。我们可以分别详细介绍每个组件,但您很快就会看到它们在交付 .NET 设计点方面进行协作。让我们从类型系统开始。

类型系统

.NET 类型系统提供了显着的广度,在某种程度上同样满足安全性、描述性、动态性和本机互操作性。

首先也是最重要的,类型系统支持面向对象的编程模型。它包括类型、(单个基类)继承、接口(包括默认方法实现)和虚拟方法分派,以为面向对象允许的所有类型分层提供合理的行为。

泛型是一项普遍的功能,允许将类专门化为一种或多种类型。例如,List<T>是一个开放的泛型类,而实例化则List<string>避免了对单独的和类List<int>的需要,或者像 的情况那样依赖和强制转换。泛型还可以跨不同类型创建有用的系统(并减少对大量代码的需求),例如Generic MathListOfStringListOfIntobjectArrayList

委托lambda允许将方法作为数据传递,从而可以轻松地将外部代码集成到另一个系统拥有的操作流中。它们是一种“粘合代码”,它们的签名通常是通用的,以实现广泛的实用性。

app.MapGet("/Product/{id}", async (int id) =>
{
    if (await IsProductIdValid(id))
    {
        return await GetProductDetails(id);
    }

    return Products.InvalidProduct;
});

lambda 的这种使用是ASP.NET Core Minimal API的一部分。它可以直接向路由系统提供端点实现。在更新的版本中,ASP.NET Core 更广泛地使用类型系统。

与 .NET 的 GC 管理类型相比,值类型堆栈分配的内存块提供了对数据和本机平台互操作更直接、低级的控制。.NET 中的大多数基本类型与整数类型一样,都是值类型,用户可以使用类似的语义定义自己的类型。

.NET 的泛型系统完全支持值类型,这意味着泛型类型List<T>可以提供值类型集合的平面、无开销内存表示。此外,.NET 泛型在替换值类型时提供专门的编译代码,这意味着这些泛型代码路径可以避免昂贵的 GC 开销。

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 语言来说也是如此。

众所周知,堆损坏错误很难调试。工程师花费数周甚至数月时间来追踪这些问题的情况并不少见。许多语言使用垃圾收集器作为消除这些错误的用户友好方式,因为 GC 可确保正确的对象生存期。通常,GC 会批量释放内存以高效运行。如果您有非常严格的延迟要求,这会导致暂停,这可能不适合,并且内存使用率会更高。GC 往往具有更好的内存局部性,并且有些 GC 能够压缩堆,使其不易产生内存碎片

.NET 有一个自调整、跟踪 GC。它的目标是在一般情况下提供“放手”操作,同时为更极端的工作负载提供配置选项。GC 是多年投资、改进和从多种工作负载中学习的结果。

碰撞指针分配——通过将分配指针增加所需的大小来分配对象(而不是在隔离的空闲块中查找空间),因此那些分配在一起的对象往往会保持在一起。由于它们经常一起访问,因此可以实现更好的内存局部性,这对性能很重要。

分代集合——对象的生命周期遵循分代假设是非常常见的,即对象要么存活很长时间,要么很快死亡。因此,对于 GC 来说,在运行的大部分时间里只收集临时对象占用的内存(称为“临时 GC”),而不是每次运行时都收集整个堆(称为“完整 GC ”) ,效率要高得多。

压缩——相同数量的可用空间在更大和更少的块中比在更小和更多的块中更有用。在压缩 GC 期间,幸存的对象会一起移动,以便形成更大的可用空间。这比非移动 GC 更难实现,因为它需要更新对这些移动对象的引用。仅当 .NET GC 确定回收的内存值得 GC 成本时,才会动态调整以执行压缩。这意味着临时集合通常会被压缩。

并行— GC 工作可以在单线程或多线程上运行。工作站风格在单个线程上执行 GC,而服务器风格在多个 GC 线程上执行,因此可以更快地完成。服务器 GC 还可以适应更大的分配率,因为应用程序可以在多个堆上进行分配,而不是只有一个堆,因此这对于吞吐量非常有利。

并发——在用户线程暂停时执行 GC 工作——称为“Stop-The-World ”——使实现更简单,但这些暂停的长度可能是不可接受的。.NET 提供了并发风格来缓解这个问题。

固定- .NET GC 支持对象固定,从而实现与本机代码的零拷贝互操作。此功能可实现高性能和高保真本机互操作,并且 GC 的开销有限。

独立GC——可以使用具有不同实现的独立GC(通过配置指定并满足接口要求)。这使得调查和尝试新功能变得更加容易。

诊断— GC 提供有关内存和集合的丰富信息,其结构允许您将数据与系统的其余部分关联起来。例如,您可以通过捕获 GC 事件并将其与其他事件(例如 IO)关联起来来计算 GC 相对于其他因素的贡献程度,从而评估尾部延迟对 GC 的影响,以便您可以将工作导向正确的组件。

安全

编程安全一直是过去十年的热门话题之一。它是 .NET 等托管环境的固有组件。

安全形式:

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

注:美国联邦政府最近发布了有关内存安全重要性的指南。

.NET 从最初的设计就被设计为一个安全的平台。特别是,它的目的是支持新一代网络服务器,这些服务器本质上需要在世界上最恶劣的计算环境(互联网)中接受不受信任的输入。现在人们普遍认为网络程序应该用安全语言编写。

类型安全是通过语言和运行时的组合来强制执行的。编译器会验证静态不变量,例如分配不同的类型(例如分配stringStream),这将产生编译器错误。运行时验证动态不变量,例如不同类型之间的转换,这将产生InvalidCastException

内存安全主要是通过代码生成器(如 JIT)和垃圾收集器之间的合作来提供的。变量要么引用活动对象,null要么超出范围。默认情况下,内存是自动初始化的,因此新对象不会使用未初始化的内存。边界检查确保访问具有无效索引的元素将不允许读取未定义的内存(通常由相差一错误引起),而是会导致IndexOutOfRangeException

null处理是内存安全的一种特定形式。可空引用类型是一项 C# 语言和编译器功能,可静态标识未安全处理null. 特别是,如果您取消引用可能为 null 的变量,编译器会发出警告。您还可以禁止null赋值,这样,如果您从可能为 null 的值分配变量,编译器会发出警告。运行时具有匹配的动态验证功能,可以通过抛出NullReferenceExceptionnull来防止引用被访问。

此功能依赖于库中可为空的属性。它还依赖于库和应用程序堆栈中的详尽应用程序,以便可以为用户代码提供静态分析工具的准确结果。

string? SomeMethod() => null;
string value = SomeMethod() ?? "default string";

C# 编译器认为此代码是 null 安全的,因为nulluse 部分是由 null 合并运算符 声明和??处理。该value变量将始终为非空,与其声明相匹配。

.NET 中没有内置的并发安全性。相反,开发人员需要遵循模式和约定以避免未定义的行为。.NET 生态系统中还有分析器和其他工具,可以深入了解并发问题。核心库包含多种可以安全并发使用的类型和方法,例如支持任意数量并发读取器和写入器的并发集合,而不会带来数据结构损坏的风险。

运行时公开安全和不安全的代码模型。安全代码的安全性得到保证,这是默认的,而开发人员必须选择使用不安全代码。不安全代码通常用于与底层平台互操作、与硬件交互或对性能关键路径实施手动优化。

 是一种特殊的安全形式,它提供隔离并限制组件之间的访问。我们依赖标准隔离技术,例如进程(和 CGroup)、虚拟机和 Wasm(具有不同的特性)。

错误处理

异常是 .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返回 a bool,其out参数包含解析的有效整数。Dictionary<TKey, TValue>.TryGetValue提供了类似的模型,返回有效TValue类型作为案例out中的参数true

错误处理和更一般的诊断是通过低级运行时 API、高级库工具来实现的。这些功能旨在支持容器等较新的部署选项。例如,dotnet-monitor可以通过内置的面向诊断的 Web 服务器将运行时数据从应用程序导出到侦听器。

并发性

支持同时执行多项操作是几乎所有工作负载的基础,无论是在保持 UI 响应的同时进行后台处理的客户端应用程序、处理成千上万个同时请求的服务、响应大量同时刺激的设备还是高并行处理计算密集型操作的机器。操作系统通过线程提供对这种并发性的支持,这使得多个指令流能够独立处理,操作系统管理这些线程在机器中任何可用处理器核心上的执行。操作系统还提供对 I/O 操作的支持,提供了使 I/O 能够以可扩展的方式执行的机制,其中许多 I/O 操作在任何特定时间“进行中”。然后,编程语言和框架可以在此核心支持之上提供各种抽象级别。

.NET 通过库并深度集成到 C# 中,在多个抽象级别提供此类并发和并行化支持。类Thread位于层次结构的底部,代表操作系统线程,使开发人员能够创建新线程并随后加入它们。ThreadPool位于线程之上,允许开发人员考虑异步安排在线程池上运行的工作项,并管理这些线程(包括从池中添加和删除线程,以及分配工作)这些线程的项目)留给运行时。Task然后为任何异步执行的操作提供统一的表示,并且可以通过多种方式创建和连接;例如,Task.Run允许安排委托在 上运行ThreadPool并返回 aTask来表示该工作的最终完成,同时Socket.ReceiveAsync返回 a Task<int>(或ValueTask<int>)来表示异步 I/O 的最终完成,以从 中读取挂起或未来的数据Socket。提供了大量同步原语,用于协调线程和异步操作之间的同步和异步活动,并且提供了大量高级 API 来简化常见并发模式的实现,例如,Parallel.ForEachParallel.ForEachAsync容易处理 a 的所有元素。并行数据序列。

异步编程支持也是 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 编译,有时用于用专用算法替换通用算法。一旦对象模型和其他细节已知,它通常用于序列化器或对象关系映射器。

编译后的二进制格式

应用程序和库被编译为PE/COFF 格式的标准化平台字节码。二进制分发最重要的是性能特征。它使应用程序能够扩展到越来越多的项目。每个库都包含一个导入和导出类型的数据库(称为元数据),它对于开发操作和运行应用程序都起着重要作用。

编译后的二进制文件主要包括两个方面:

  • 二进制字节码 — 简洁且常规的格式,无需在高级语言编译器(如 C#)编译后解析文本源。
  • 元数据 - 描述导入和导出的类型,包括给定方法的字节代码的位置。

例如,对于开发来说,工具可以有效地读取元数据,以确定给定库公开的类型集以及这些类型中哪些类型实现某些接口。此过程使编译速度更快,并使 IDE 和其他工具能够准确地呈现给定上下文的类型和成员列表。

对于运行时,元数据使库能够延迟加载,方法体更是如此。反射(稍后讨论)是元数据和 IL 的运行时 API。还有其他更合适的 API 工具。

随着时间的推移,IL 格式一直保持向后兼容。最新的 .NET 版本仍然可以加载和执行使用 .NET Framework 1.0 编译器生成的二进制文件。

共享库通常通过NuGet 包分发。默认情况下,带有单个二进制文件的 NuGet 包可以在任何操作系统和体系结构上工作,但也可以专门用于在特定环境中提供特定行为。

代码生成

.NET 字节码不是机器可执行格式,但需要通过某种形式的代码生成器使其可执行。这可以通过提前 (AOT) 编译、即时 (JIT) 编译、解释或转译来实现。事实上,这些现在都在各种场景中使用。

.NET 最著名的是 JIT 编译。JIT 在应用程序运行时将方法(和其他成员)编译为本机代码,并且仅在需要时才将其编译为本机代码,因此称为“及时”。例如,程序在运行时可能仅调用某个类型的多个方法之一。JIT 还可以利用仅在运行时可用的信息,例如初始化的只读静态变量的值或程序运行时的确切 CPU 模型,并且可以多次编译相同的方法,以便每次针对不同的情况进行优化目标以及从以前的汇编中学到的知识。

JIT 为给定的操作系统和芯片架构生成代码。.NET 具有支持 Arm64 和 x64 指令集以及 Linux、macOS 和 Windows 操作系统等的 JIT 实现。作为 .NET 开发人员,您不必担心 CPU 指令集和操作系统调用约定之间的差异。JIT 负责生成 CPU 想要的代码。它还知道如何为每个 CPU 生成快速代码,操作系统和 CPU 供应商经常帮助我们做到这一点。

AOT 类似,只不过代码是在程序运行之前生成的。开发人员选择此选项是因为它可以通过消除 JIT 完成的工作来显着缩短启动时间。AOT 构建的应用程序本质上是特定于操作系统和架构的,这意味着需要额外的步骤才能使应用程序在多个环境中运行。例如,如果您想支持 Linux 和 Windows 以及 Arm64 和 x64,那么您需要构建四个变体(以允许所有组合)。AOT 代码也可以提供有价值的优化,但一般不如 JIT 那么多。

我们将在后面的文章中介绍解释和转译,但是它们在我们的生态系统中也发挥着关键作用。

代码生成器优化之一是内在函数。硬件内在函数是.NET API直接转换为 CPU 指令的示例。这已在 .NET 库中广泛用于SIMD指令。

互操作性

.NET 专门为与本机库的低成本互操作而设计。.NET 程序和库可以无缝调用低级操作系统 API 或利用庞大的 C/C++ 库生态系统。现代 .NET 运行时专注于提供低级互操作构建块,例如通过函数指针调用本机方法的能力、将托管方法公开为非托管回调自定义接口转换。.NET 也在这一领域不断发展,在 .NET 7 中发布了源生成的解决方案,进一步减少了开销并且对 AOT 友好。

下面演示了使用 .NET 7 中引入的源生成器(此源生成器支持层位于自 .NET 诞生以来就存在的支持LibraryImport之上)的 C# 函数指针的效率。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);

独立包通过利用这些低级构建块(例如ClangSharpXamarin.iOS 和 Xamarin.MacCsWinRTCsWin32DNNE )提供更高级别的特定于域的互操作解决方案。

这些新功能并不意味着内置的互操作解决方案(例如内置运行时托管/非托管编组或 Windows COM 互操作)没有用处 — 我们知道它们很有用,而且人们已经开始依赖它们。.NET 运行时继续支持历史上内置于运行时中的那些功能。然而,它们只是为了向后兼容,没有计划进一步发展它们。未来的所有投资都将集中在互操作构建块及其支持的特定领域解决方案上。

二进制发行版

Microsoft 的 .NET 团队维护多个二进制发行版,最近支持 Android、iOS 和Web Assembly。该团队使用各种技术来专门针对每种环境的代码库。该平台的大部分内容都是用 C# 编写的,这使得移植能够集中在相对较小的组件集上。

社区维护着另一组发行版,主要集中在 Linux 上。例如,.NET 包含在Alpine LinuxFedoraRed Hat Enterprise LinuxUbuntu中。

社区还扩展了 .NET 以在其他平台上运行。三星为其基于 Arm 的 Tizen 平台移植了 .NETRed HatIBM 将 .NET 移植到 LinuxONE/s390x。 龙芯科技将.NET移植到LoongArch。我们希望并期望新的合作伙伴将 .NET 移植到其他环境。

Unity Technologies 已启动一项为期多年的计划,以实现 .NET 运行时的现代化。

.NET开源项目的维护和构建使个人、公司和其他组织能够在传统的上游模型中进行协作。Microsoft 是该平台的管理者,提供项目治理和项目基础设施(如 CI 管道)。Microsoft 团队与组织合作,帮助他们成功使用和/或移植 .NET。该项目有广泛的上游政策,其中包括接受给定发行版特有的更改。

主要焦点是源构建项目多个组织使用该项目根据典型的发行版规则(例如Canonical (Ubuntu))构建 .NET 。最近,随着Virtual Mono Repo (VMR)的增加,这一焦点得到了扩展。.NET 项目由许多存储库组成,这有助于提高 .NET 开发人员的效率,但也使构建完整产品变得更加困难。VMR 解决了这个问题。

概括

我们已经进入现代 .NET 时代的几个版本,最近发布了.NET 7。我们认为,如果我们总结一下自 .NET Core 1.0 以来我们一直在平台最低级别构建的内容,将会很有用。虽然我们明确地保留了原始 .NET 的精神,但结果是一个新平台开辟了一条新道路,并为开发人员提供了新的、更多的价值。

让我们从开始的地方结束吧。.NET 代表四个价值:生产力、性能、安全性和可靠性。我们坚信,当不同的语言平台提供不同的方法时,开发人员会得到最好的服务。作为一个团队,我们致力于为 .NET 开发人员提供高生产力,同时提供一个在性能、安全性和可靠性方面领先的平台。

我们计划在本系列中添加更多帖子。您希望首先讨论哪些主题?请在评论中告诉我们。您想了解更多此类“大局”内容吗?

如果您想了解更多此类内容,可以查看公共语言运行时 (CLR) 简介

这篇文章由Jan KotasRich LanderMaoni StephensStephen Toub撰写,并结合了 .NET 团队同事的见解和评论。

文章作者 | .NET Team(.NET团队)

  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值