Pro C#10 CHAPTER 1 Introducing C# and .NET 6

Pro C# 10 with .NET 6

PART I

Introducing C# and .NET 6
介绍 C# 和 .NET 6

CHAPTER 1

第一章

Introducing C# and .NET 6
介绍 C# 和 .NET 6

Microsoft’s .NET platform and the C# programming language were formally introduced circa 2002 and have quickly become a mainstay of modern-day software development. The .NET platform enables a large number of programming languages (including C#, VB.NET, and F#) to interact with each other. A program written in C# can be referenced by another program written in VB.NET. More on this interoperability later in this chapter.
Microsoft的.NET平台和C#编程语言大约在2002年正式推出,并迅速成为现代软件开发的支柱。.NET 平台使大量编程语言(包括 C#、VB.NET 和 F#)能够相互交互。一个程序用 C# 编写的程序可以被另一个用 VB.NET 编写的程序引用。本章后面将详细介绍此互操作性。

In 2016, Microsoft officially launched .NET Core. Like .NET, .NET Core allows languages to interop with each other (although a limited number of languages are supported). More importantly, this new framework is no longer limited to running on the Windows operating system but can also run on iOS, and Linux and be developed on MacOS and Linux. This platform independence opened up .NET and C# to a much larger pool of developers. While cross-platform use of C# was supported prior to .NET Core, that was through various other frameworks such as the Mono project.
2016年,Microsoft正式推出.NET Core。与 .NET 一样,.NET Core 允许语言相互互操作(尽管支持的语言数量有限)。更重要的是,这个新框架不再局限于在Windows操作系统上运行,还可以在iOS和Linux上运行,并在MacOS和Linux上开发。这种平台独立性向更多的开发人员开放了 .NET 和 C#。虽然在.NET Core之前支持跨平台使用C#,但这是通过各种其他框架(如Mono项目)支持的。

■ Note
With the release of .NET 5, the “Core” part of the name was dropped. Throughout this book, the term .NET refers to .NET Core (up to 3.1) and .NET 5/6
注意 随着 .NET 5 的发布,名称的“核心”部分被删除。在整本书中,术语.NET 是指 .NET Core(最高 3.1)和 .NET 5/6。

Microsoft launched C# 10 and .NET 6 on November 8, 2021. C# 10 is tied to a specific version of the framework and will run only on .NET 6 and above. This relationship between language and .NET versions gives the C# team the freedom to introduce features into C# that couldn’t otherwise be added into the language due to framework limitations.
Microsoft于 10 年 6 月 8 日推出了 C# 10 和 .NET6。C# 10 绑定到特定版本的框架,并且只能在 .NET6及更高版本上运行。语言和 .NET 版本之间的这种关系使 C# 团队可以自由地将由于框架限制而无法添加到语言中的功能引入 C#。

As mentioned in the book’s introduction, the goal of this text is twofold. The first order of business is to provide you with a deep and detailed examination of the syntax and semantics of C#. The second (equally important) order of business is to illustrate the use of numerous .NET development frameworks. These include database access with ADO.NET and Entity Framework (EF) Core, user interfaces with Windows Presentation Foundation (WPF), and finally RESTful services and web applications with ASP.NET Core. As it is said, the journey of a thousand miles begins with a single step; and with this, I welcome you to Chapter 1.
正如本书的引言中提到的,本文的目标是双重的。首要任务是为您提供对 C# 语法和语义的深入而详细的检查。第二个(同样重要)的业务顺序是说明许多 .NET 开发框架的使用。其中包括使用 ADO.NET 和实体框架 (EF) 核心访问数据库、使用 Windows Presentation Foundation (WPF) 的用户界面,以及最后使用 ASP.NET Core 的 RESTful 服务和 Web 应用程序。俗话说,千里之行,始于足下;有了这个,我欢迎你来到第一章。

This first chapter lays the conceptual groundwork for the remainder of the book. Here, you will find a high-level discussion of a number of .NET-related topics such as assemblies, the Common Intermediate Language (CIL), and just-in-time (JIT) compilation. In addition to previewing some keywords of the C# programming language, you will also come to understand the relationship between the .NET Runtime, the Common Type System (CTS) and the Common Language Specification (CLS).
第一章为本书的其余部分奠定了概念基础。在这里,您将找到一些 .与 NET 相关的主题,例如程序集、公共中间语言 (CIL) 和实时 (JIT) 编译。除了预览 C# 编程语言的一些关键字外,您还将了解 .NET 运行时、通用类型系统 (CTS) 和公共语言规范 (CLS) 之间的关系。

This chapter also provides you with a survey of the functionality supplied by the .NET base class libraries, sometimes abbreviated as BCLs. Here, you will get an overview of the language-agnostic and platform-independent nature of the .NET platform. As you would expect, these topics are explored in further detail throughout the remainder of this text.
本章还概述了 .NET 基类库(有时缩写为 BCL)提供的功能。在这里,你将大致了解 .NET 平台与语言无关且独立于平台的性质。如您所料,本文的其余部分将更详细地探讨这些主题。

■ Note
Many of the features highlighted in this chapter (and throughout the book) also apply to the original.NET Framework.
注意 本章(以及整本书)中强调的许多功能也适用于原文.NET Framework.

Exploring Some Key Benefits of the .NET Platform

探索 .NET 平台的一些主要优势

The .NET framework is a software platform for building web applications and services for the Windows, iOS, and Linux operating systems, as well as WinForms and WPF applications on Windows operating systems. To set the stage, here is a quick rundown of some core features provided, courtesy of .NET:
NET 框架是一个软件平台,用于为 Windows、iOS 和 Linux 操作系统以及 Windows 操作系统上的 WinForms 和 WPF 应用程序构建 Web 应用程序和服务。为了奠定基础,下面是由 .NET 提供的一些核心功能的快速概述:

  • Support for numerous programming languages: .NET applications can be created using C#, F#, and VB.NET programming languages (with C# and F# being the primary languages for ASP.NET Core).
    支持多种编程语言:可以使用 C#、F# 和 VB.NET 编程语言(C# 和 F# 是 ASP.NET Core 的主要语言)创建 .NET 应用程序。
  • A common runtime engine shared by all .NET languages: One aspect of this engine is a well-defined set of types that each .NET language understands.
    所有 .NET 语言共享的通用运行时引擎:此引擎的一个方面是一组定义良好的类型,每种 .NET 语言都能理解这些类型。
  • Language integration: .NET supports cross-language inheritance, cross-language exception handling, and cross-language debugging of code. For example, you can define a base class in C# and extend this type in Visual Basic.
    语言集成:.NET 支持跨语言继承、跨语言异常处理和跨语言代码调试。例如,可以在 C# 中定义基类,并在 Visual Basic 中扩展此类型。
  • A comprehensive base class library: This library provides thousands of predefined types that allow you to build code libraries, simple terminal applications, graphical desktop applications, and enterprise-level websites.
    全面的基类库:此库提供数千种预定义类型,允许您构建代码库、简单终端应用程序、图形桌面应用程序和企业级网站。
  • A simplified deployment model: .NET libraries are not registered into the system registry. Furthermore, the .NET platform allows multiple versions of the framework as well as applications to exist in harmony on a single machine.
    简化的部署模型:.NET 库未注册到系统注册表中。此外,.NET 平台允许框架的多个版本以及应用程序在一台机器上和谐存在。
  • Extensive command-line support: The .NET command-line interface (CLI) is a cross- platform tool chain for developing and packaging .NET applications. Additional tools can be installed (globally or locally) beyond the standard tools that ship with the .NET SDK.
    广泛的命令行支持:.NET 命令行界面 (CLI) 是用于开发和打包 .NET 应用程序的跨平台工具链。除了 .NET SDK 附带的标准工具之外,还可以安装其他工具(全局或本地)。

You will see each of these topics (and many more) examined in the chapters to come. But first, I need to explain the new support lifecycle for .NET.
您将在接下来的章节中看到这些主题中的每一个(以及更多)。但首先,我需要解释 .NET 的新支持生命周期。

Understanding the .NET Support Lifecycle

了解.NET 支持生命周期

.NET versions are released much more frequently than prior .NET Framework. With all of these releases available, it can be difficult to keep up, especially in an enterprise development environment. To better define the support lifecycle for the releases, Microsoft has adopted a variation of the Long-Term Support Model,1 commonly used by modern open source frameworks.
.NET 版本的发布频率比以前的 .NET Framework 高得多。由于所有这些版本都可用,因此可能很难跟上,尤其是在企业开发环境中。为了更好地定义版本的支持生命周期,Microsoft采用了现代开源框架常用的长期支持模型1的变体。

Long-Term Support (LTS) releases are major releases that will be supported for an extended period of time. They will only receive critical and/or nonbreaking fixes throughout their life span. Prior to end-of-life, LTS versions will be changed to the designation of maintenance. LTS releases with .NET will be supported for the following time frames, whichever is longer:
长期支持 (LTS) 版本是将长期支持的主要版本。他们只会在其整个生命周期内收到关键和/或不间断的修复。在使用寿命结束之前,LTS 版本将更改为维护指定。在以下时间范围内,将支持带有 .NET 的 LTS 版本,以较长者为准:

  • Three years after initial release
    首次发布三年后
  • One year of maintenance support after subsequent LTS release
    后续 LTS 发布后一年的维护支持

Microsoft has decided to name Short-Term Support releases as Current, which are interval releases between the major LTS releases. They are supported for six months after a subsequent Current or LTS release.
Microsoft已决定将短期支持版本命名为当前版本,这是主要 LTS 版本之间的间隔版本。它们在后续当前或 LTS 版本发布后的六个月内受支持。

As mentioned earlier, .NET 6 was released in November 2021. It was released as a Long Term Support version and will be supported at least until November 2024. .NET 5 will go out of support on May 8, 2022, six months after the .NET 6 release. The only other supported version at the time of this writing is .NET Core 3.1 which is supported until December 3, 2022.
如前所述,.NET 6于2021年11月发布。它作为长期支持版本发布,至少在2024年11月之前将受支持。.NET 5将于2022年5月停止支持,即.NET 6发布3个月后。在撰写本文时,唯一受支持的其他版本是 .NET Core 3.1,该版本在2022年12月3日之前受支持。

It’s important to check the support policy for each new version of .NET that is released. Just having a higher number doesn’t necessarily mean it’s going to be supported long term. The full policy is located here:
请务必检查发布的每个新版本 .NET 的支持策略。仅仅拥有更高的数字并不一定意味着它将得到长期支持。完整政策位于此处:
https://dotnet.microsoft.com/platform/support-policy/dotnet-core

Previewing the Building Blocks of the .NET Platform

预览 .NET 平台的构建基块

Now that you know some of the major benefits provided by .NET, let’s preview key (and interrelated) topics that make it all possible: the .NET Runtime, CTS, and the CLS. From a programmer’s point of view, .NET can be understood as a runtime environment and a comprehensive base class library. The runtime layer contains the set of minimal implementations that are tied specifically to a platform (Windows, iOS, Linux) and architecture (x86, x64, ARM), as well as all of the base types for .NET.
现在,您已经了解了 .NET 提供的一些主要优势,让我们预览一下使这一切成为可能的关键(和相互关联)主题:.NET 运行时、CTS 和 CLS。从程序员的角度来看,.NET 可以理解为运行时环境和全面的基类库。运行时层包含一组专门绑定到平台(Windows、iOS、Linux)和体系结构(x86、x64、ARM)的最小实现,以及 .NET 的所有基本类型。

Another building block of the .NET platform is the Common Type System, or CTS. The CTS specification fully describes all possible data types and all programming constructs supported by the runtime, specifies how these entities can interact with each other, and details how they are represented in the .NET metadata format (more information on metadata later in this chapter; see Chapter 17 for complete details).
.NET 平台的另一个构建基块是通用类型系统或 CTS。CTS 规范完整描述了运行时支持的所有可能的数据类型和所有编程构造,指定了这些实体如何相互交互,并详细说明了它们如何以 .NET 元数据格式表示(有关本章后面的元数据的详细信息;有关完整详细信息,请参阅第 17 章)。

Understand that a given .NET language might not support every feature defined by the CTS. The Common Language Specification, or CLS, is a related specification that defines a subset of common types and programming constructs that all .NET programming languages can agree on. Thus, if you build .NET types that expose only CLS-compliant features, you can rest assured that all .NET languages can consume them. Conversely, if you make use of a data type or programming construct that is outside of the bounds of the CLS, you cannot guarantee that every .NET programming language can interact with your .NET code library. Thankfully, as you will see later in this chapter, it is simple to tell your C# compiler to check all of your code for CLS compliance.
了解给定的 .NET 语言可能不支持 CTS 定义的所有功能。公共语言规范 (CLS) 是一个相关规范,它定义所有 .NET 编程语言都可以同意的常见类型和编程构造的子集。因此,如果生成仅公开符合 CLS 的功能的 .NET 类型,则可以放心,所有 .NET 语言都可以使用它们。相反,如果使用 CLS 边界之外的数据类型或编程构造,则无法保证每种 .NET 编程语言都可以与 .NET 代码库交互。值得庆幸的是,正如您将在本章后面看到的那样,告诉 C# 编译器检查所有代码是否符合 CLS 非常简单。

The Role of the Base Class Libraries

基类库的作用

The .NET platform also provides a set of base class libraries (BCLs) that are available to all .NET programming languages. Not only does this base class library encapsulate various primitives such as threads, file input/output (I/O), graphical rendering systems, and interaction with various external hardware devices, but it also provides support for a number of services required by most real-world applications.
.NET 平台还提供了一组可用于所有 .NET 编程语言的基类库 (BCL)。此基类库不仅封装了各种基元,例如线程、文件输入/输出 (I/O)、图形呈现系统以及与各种外部硬件设备的交互,而且还为大多数实际应用程序所需的许多服务提供支持。

The base class libraries define types that can be used to build any type of software application and for components of the application to interact with each other.
基类库定义可用于生成任何类型的软件应用程序以及应用程序组件相互交互的类型。

The Role of .NET Standard

.NET 标准的角色

The number of base class libraries in the .NET Framework far exceeds those in .NET. This is understandable, as the .NET Framework had a 14-year head start on .NET. This disparity created issues when attempting to use .NET Framework code with .NET code. The solution (and requirement) for .NET Framework/.NET Core3.1 interop is .NET Standard.
.NET Framework 中的基类库数量远远超过 .NET 中的基类库。这是可以理解的,因为.NET Framework在.NET上已经领先了14年。这种差异在尝试将.NET Framework代码与.NET代码一起使用时会产生问题。.NET Framework/.NET Core 的解决方案(和要求)互操作是 .NET Standard。

.NET Standard is a specification that defines the availability of .NET APIs and base class libraries that must be available in each implementation. The standard enables the following scenarios:
.NET 标准是一个规范,用于定义每个实现中必须可用的.NET API 和基类库的可用性。该标准支持以下方案:

  • Defines a uniform set of BCL APIs for all .NET implementations to implement, independent of workload
    为所有要实现的 .NET 实现定义一组统一的 BCL API,与工作负载无关
  • Enables developers to produce portable libraries that are usable across .NET implementations, using this same set of APIs
    使开发人员能够使用同一组 API 生成可跨 .NET 实现使用的可移植库
  • Reduces or even eliminates conditional compilation of shared source due to .NET APIs, only for OS APIs
    减少甚至消除由于 .NET API 而对共享源代码的条件编译,仅适用于操作系统 API

The chart located in the Microsoft documentation (https://docs.microsoft.com/en-us/dotnet/standard/net-standard) shows the various compatibility between .NET Framework and .NET. This is useful for prior versions of C#. However, C# 9+ will only run on .NET 5+ or .NET Standard 2.1, and .NET Standard 2.1 is not available to the .NET Framework.
位于Microsoft文档(https://docs.microsoft.com/en-us/dotnet/standard/net-standard )中的图表显示了.NET Framework和.NET之间的各种兼容性。这对于以前版本的 C# 很有用。但是,C# 9+ 只能在 .NET 5+ 或 .NET Standard 2.1。.NET Standard 对 .NET Framework 不可用。

What C# Brings to the Table

C# 带来了什么

C# is a programming language whose core syntax looks very similar to the syntax of Java. However, calling C# a Java clone is inaccurate. In reality, both C# and Java are members of the C family of programming languages (e.g., C, Objective-C, C++) and, therefore, share a similar syntax.
C#是一种编程语言,其核心语法看起来与Java的语法非常相似。但是,将 C# 称为 Java 克隆是不准确的。实际上,C#和Java都是C系列编程语言(例如C,Objective-C,C++)的成员,因此共享相似的语法。

The truth of the matter is that many of C#’s syntactic constructs are modeled after various aspects of Visual Basic (VB) and C++. For example, like VB, C# supports the notion of class properties (as opposed to traditional getter and setter methods) and optional parameters. Like C++, C# allows you to overload operators, as well as create structures, enumerations, and callback functions (via delegates).
事情的真相是,C#的许多语法结构都是根据Visual Basic(VB)和C++的各个方面建模的。例如,与 VB 一样,C# 支持类属性(与传统的 getter 和 setter 方法相反)和可选参数的概念。与C++一样,C# 允许您重载运算符,以及创建结构、枚举和回调函数(通过委托)。

Moreover, as you work through this text, you will quickly see that C# supports a number of features, such as lambda expressions and anonymous types, traditionally found in various functional languages (e.g., LISP or Haskell). Furthermore, with the advent of Language Integrated Query (LINQ), C# supports a number of constructs that make it quite unique in the programming landscape. Nevertheless, the bulk of C# is indeed influenced by C-based languages.
此外,当您阅读本文时,您将很快看到 C# 支持许多功能,例如 lambda 表达式和匿名类型,这些功能传统上存在于各种函数式语言(例如 LISP 或 Haskell)中。此外,随着语言集成查询 (LINQ) 的出现,C# 支持使其在编程环境中非常独特的构造数量。尽管如此,大部分 C# 确实受到基于 C 的语言的影响。

Because C# is a hybrid of numerous languages, the result is a product that is as syntactically clean as (if not cleaner than) Java provides just about as much power and flexibility as C++. Here is a partial list of core C# features that are found in all versions of the language:
由于 C# 是多种语言的混合体,因此其结果是产品在语法上与 Java 一样干净(如果不是比 Java 更干净的话),它提供的功能和灵活性与 C++ 一样多。下面是该语言的所有版本中的核心 C# 功能的部分列表:

  • No pointers required! C# programs typically have no need for direct pointer manipulation (although you are free to drop down to that level if absolutely necessary, as shown in Chapter 11).
    无需指针!C# 程序通常不需要直接指针操作(尽管如果绝对必要,您可以自由下降到该级别,如第 11 章所示)。
  • Automatic memory management through garbage collection. Given this, C# does not support a delete keyword.
    通过垃圾回收进行自动内存管理。鉴于此,C# 不支持 delete 关键字。
  • Formal syntactic constructs for classes, interfaces, structures, enumerations, and delegates.
    类、接口、结构、枚举和委托的正式语法构造。
  • The C++-like ability to overload operators for a custom type, without the complexity.
    类似C++的功能,可重载自定义类型的运算符,而没有复杂性。
  • Support for attribute-based programming. This brand of development allows you to annotate types and their members to further qualify their behavior. For example, if you mark a method with the [Obsolete] attribute, programmers will see your custom warning message print out if they attempt to make use of the decorated member.
    支持基于属性的编程。此开发品牌允许您批注类型及其成员,以进一步限定其行为。例如,如果使用 [Obsolete] 属性标记方法,则程序员在尝试使用修饰成员时将看到打印出的自定义警告消息。

C# 10 is an already powerful language and, combined with .NET, enables building a wide range of application types.
C# 10 是一种已经很强大的语言,与 .NET 结合使用,可以生成各种应用程序类型。

Major Features in Prior Releases

先前版本中的主要功能

Starting with C# 7, I started adding into the section headers the version when features were added (e.g. “(New 7.x)”) or updated (e.g. “(Updated 7.x)”).
从 C# 7 开始,我开始在节标题中添加添加功能(例如“(新 7.x)”)或更新功能(例如“(更新的 7.x)”)时的版本。

New Features in C# 10

C# 10 中的新功能

C# 10, released on November 8, 2021, with .NET 6, adds the following features:
2021年11月8日发布的 C# 6 与 .NET6一起发布,添加了以下功能:

  • Record structs
    记录结构
  • Improvements to structure types
    对结构类型的改进
  • Global using directives and global implicit using directives
    全局 using 指令和全局隐式 using 指令
  • File scoped namespaces
    文件范围的命名空间
  • Property pattern matching enhancements
    属性模式匹配增强功能
  • Constant Interpolated strings
    常量内插字符串
  • Improvements to lambda expressions
    对lambda表达式的改进
  • Record type enhancements
    记录类型增强功能
  • Assignment and declaration in deconstruction
    解构中的转让和声明
  • Removal of false warnings on definite assignment
    消除明确分配的错误警告

In addition to this, there are also updates to the .NET framework that impact development. I have updated the section headers that are new (or updated) in C#10 /NET 6 with “(New 10)” or “(Updated 10”).
除此之外,还有影响开发的 .NET 框架更新。我已经将 C#10 /NET 6 中新增(或更新)的部分标题更新为“(新 10)”或“(更新 10”)。

Managed vs. Unmanaged Code

托管代码与非托管代码

It is important to note that the C# language can be used only to build software that is hosted under the .NET runtime (you could never use C# to build a native COM server or an unmanaged C/C++-style application). Officially speaking, the term used to describe the code targeting the .NET runtime is managed code. The binary unit that contains the managed code is termed an assembly (more details on assemblies in just a bit). Conversely, code that cannot be directly hosted by the .NET runtime is termed unmanaged code.
请务必注意,C# 语言只能用于生成在 .NET 运行时下承载的软件(永远不能使用 C# 生成本机 COM 服务器或非托管 C/C++ 样式的应用程序)。正式地说,用于描述面向 .NET 运行时的代码的术语是托管代码。包含托管代码的二进制单元称为程序集(稍后将详细介绍程序集)。相反,不能由 .NET 运行时直接承载的代码称为非托管代码。

As mentioned previously, the .NET platform can run on a variety of operating systems. Thus, it is quite possible to build a C# application on a Windows machine and run the program on an iOS machine using the .NET runtime. As well, you can build a C# application on Linux using Visual Studio Code and run the program on Windows. With Visual Studio for Mac, you can also build .NET applications on a Mac to be run on Windows, macOS, or Linux.
如前所述,.NET 平台可以在各种操作系统上运行。因此,很有可能在 Windows 计算机上生成 C# 应用程序并使用 .NET 运行时在 iOS 计算机上运行该程序。同样,您可以使用Visual Studio Code在Linux上构建C#应用程序,并在Windows上运行该程序。使用 Visual Studio for Mac,您还可以在 Mac 上构建 .NET 应用程序,以便在 Windows、macOS 或 Linux 上运行。

Unmanaged code can still be accessed from a C# program, but it then locks you into a specific development and deployment target.
仍然可以从 C# 程序访问非托管代码,但随后它会将您锁定到特定的开发和部署目标。

Using Additional .NET–Aware Programming Languages

使用其他 .NET 感知编程语言

Understand that C# is not the only language that can be used to build .NET applications. .NET applications can generally be built with C#, Visual Basic, and F#, which are the three languages supported directly by Microsoft.
了解 C# 并不是可用于生成 .NET 应用程序的唯一语言。.NET 应用程序通常可以使用 C#、Visual Basic 和 F# 构建,这是 Microsoft直接支持的三种语言。

Getting an Overview of .NET Assemblies

获取 .NET 程序集的概述

Regardless of which .NET language you choose to program with, understand that despite .NET binaries taking the same file extension as unmanaged Windows binaries (.dll), they have absolutely no internal similarities. Specifically, .NET binaries do not contain platform-specific instructions but rather platform- agnostic Intermediate Language (IL) and type metadata.
无论选择使用哪种 .NET 语言进行编程,请了解,尽管 .NET 二进制文件采用与非托管 Windows 二进制文件相同的文件扩展名 (
.dll),但它们绝对没有内部相似之处。具体而言,.NET 二进制文件不包含特定于平台的指令,而是包含与平台无关的中间语言 (IL) 和类型元数据。

■ Note
IL is also known as Microsoft Intermediate Language (MSIL) or alternatively as the Common Intermediate Language (CIL). Thus, as you read the .NET literature, understand that IL, MSIL, and CIL are all describing essentially the same concept. In this book, I will use the abbreviation CIL to refer to this low-level instruction set.
注意 IL 也称为Microsoft中间语言 (MSIL) 或公共中间语言 (CIL)。因此,在阅读 .NET 文献时,请了解 IL、MSIL 和 CIL 本质上都描述了相同的概念。在本书中,我将使用缩写CIL来指代这个低级指令集。

When a .dll has been created using a .NET compiler, the binary blob is termed an assembly. You will examine numerous details of .NET assemblies in Chapter 16. However, to facilitate the current discussion, you do need to understand four basic properties of this new file format.
使用 .NET 编译器创建
.dll 时,二进制 blob 称为程序集。您将在第 16 章中研究 .NET 程序集的大量细节。但是,为了便于当前的讨论,您确实需要了解这种新文件格式的四个基本属性。

First, unlike .NET Framework assemblies that can be either a .dll or .exe, .NET projects are always compiled to a file with a .dll extension, even if the project is an executable. Executable .NET assemblies are executed with the command dotnet .dll. New in .NET Core 3.0 (and later), the dotnet.exe command is copied to the build directory and renamed to .exe. Running this command automatically calls the dotnet .dll file, executing the equivalent of dotnet .dll. The .exe with your project name isn’t actually your project’s code; it is a convenient shortcut to running your application.
首先,与可以是
.dll 或 .exe 的 .NET Framework 程序集不同,.NET 项目始终编译为扩展名为 .dll 的文件,即使该项目是可执行文件也是如此。可执行的 .NET 程序集使用命令 dotnet<程序集名称>.dll执行。dotnet.exe 命令是 .NET Core 3.0(及更高版本)中的新增功能,它被复制到生成目录并重命名为 <程序集名称>.exe。运行此命令会自动调用 dotnet<程序集名称>.dll文件,执行等效的 dotnet<程序集名称>.dll。带有项目名称的 .exe 实际上并不是项目代码;它是运行应用程序的便捷快捷方式。

Updated in .NET 6, your application can be reduced to a single file that is executed directly. Even though this single file looks and acts like a C++-style native executable, the single file is a packaging convenience. It contains all the files needed to run your application, potentially even the .NET runtime itself! But know that your code is still running in a managed container just as if it were published as multiple files.
在 .NET 6 中更新,可以将应用程序缩减为直接执行的单个文件。尽管此单个文件的外观和行为类似于 C++ 样式的本机可执行文件,但单个文件是一种打包便利。它包含运行应用程序所需的所有文件,甚至可能包含 .NET 运行时本身!但要知道,您的代码仍在托管容器中运行,就像它作为多个文件发布一样。

Second, an assembly contains CIL code, which is conceptually similar to Java bytecode, in that it is not compiled to platform-specific instructions until absolutely necessary. Typically, “absolutely necessary” is the point at which a block of CIL instructions (such as a method implementation) is referenced for use by the.NET runtime.
其次,程序集包含 CIL 代码,这在概念上类似于 Java 字节码,因为它不会编译为特定于平台的指令,除非绝对必要。通常,“绝对必要”是引用 CIL 指令块(例如方法实现)以供.NET 运行时。

Third, assemblies also contain metadata that describes in vivid detail the characteristics of every “type” within the binary. For example, if you have a class named SportsCar, the type metadata describes details such as SportsCar’s base class, specifies which interfaces are implemented by SportsCar (if any), and gives full descriptions of each member supported by the SportsCar type. .NET metadata is always present within an assembly and is automatically generated by the language compiler.
第三,程序集还包含元数据,这些元数据生动详细地描述了二进制文件中每个“类型”的特征。例如,如果您有一个名为 SportsCar 的类,则类型元数据描述 SportsCar 基类等详细信息,指定 SportsCar 实现的接口(如果有),并提供 SportsCar 类型支持的每个成员的完整说明。 .NET 元数据始终存在于程序集中,并由语言编译器自动生成。

Finally, in addition to CIL and type metadata, assemblies themselves are also described using metadata, which is officially termed a manifest. The manifest contains information about the current version of the assembly, culture information (used for localizing string and image resources), and a list of all externally referenced assemblies that are required for proper execution. You’ll examine various tools that can be used to examine an assembly’s types, metadata, and manifest information over the course of the next few chapters.
最后,除了 CIL 和类型元数据之外,程序集本身也使用元数据进行描述,正式称为清单。清单包含有关程序集当前版本的信息、区域性信息(用于本地化字符串和图像资源)以及正确执行所需的所有外部引用程序集的列表。您将检查各种工具用于在接下来的几章中检查程序集的类型、元数据和清单信息。

The Role of the Common Intermediate Language

公共中间语言的作用

Let’s examine CIL code, type metadata, and the assembly manifest in a bit more detail. CIL is a language that sits above any particular platform-specific instruction set. For example, the following C# code models a trivial calculator. Don’t concern yourself with the exact syntax for now, but do notice the format of the Add() method in the Calc class.
让我们更详细地检查 CIL 代码、类型元数据和程序集清单。CIL 是一种位于任何特定平台特定指令集之上的语言。例如,以下 C# 代码对一个简单的计算器进行了建模。现在不要关心确切的语法,但请注意 Calc 类中 Add() 方法的格式。

```
//Clac.cs or Program.cs
//在Program.cs文件中编写代码或者单独编写Clac.cs文件

Calc c = new Calc();
int ans = c.Add(10, 84);
Console.WriteLine("10+84 is {0} .",ans);

// Wait for user to press the Enter key
// 等待用户按下Enter键
Console.ReadLine();

// The C# Calculator .
// C# 代码计算器
class Calc
{
    public int Add(int addend1, int addend2)
    {
        return addend1 + addend2;
    }
}
```

Compiling this code produces a file .dll assembly that contains a manifest, CIL instructions, and metadata describing each aspect of the Calc and Program classes.
编译此代码会生成一个文件
.dll 程序集,其中包含清单、CIL 指令和描述 Calc 和 Program 类各个方面的元数据。

■ Note
Chapter 2 examines how to use graphical integrated development environments (IdEs), such as Visual Studio Community, to compile your code files.
注意 第 2 章探讨如何使用图形集成开发环境 (IdE)(如 Visual Studio Community)来编译代码文件。

For example, if you were to output the IL from this assembly using ildasm.exe (examined later in this chapter), you would find that the Add() method is represented using CIL such as the following:
例如,如果要使用 ildasm.exe(在本章后面进行检查)从此程序集输出 IL,您会发现 Add() 方法是使用 CIL 表示的,如下所示:

.method public hidebysig instance int32 Add(int32 addend1,
              int32 addend2) cil managed
{
// Method begins at RVA 0x2090
// Code size        9 (0x9)
.maxstack 2
.locals /*11000002*/ init (int32 V_0)
IL_0000: /* 00 |        */ nop
IL_0001: /* 03 |        */ ldarg.1
IL_0002: /* 04 |        */ ldarg.2
IL_0003: /* 58 |        */ add
IL_0004: /* 0A |        */ stloc.0
IL_0005: /* 2B | 00     */ br.s IL_0007
IL_0007: /* 06 |        */ ldloc.0
IL_0008: /* 2A |        */ ret
} // end of method Calc::Add

Don’t worry if you are unable to make heads or tails of the resulting CIL for this method because Chapter 18 will describe the basics of the CIL programming language. The point to concentrate on is that the C# compiler emits CIL, not platform-specific instructions.
如果您无法为此方法生成 CIL 的正面或反面,请不要担心,因为第 18 章将描述 CIL 编程语言的基础知识。需要关注的一点是 C# 编译器发出 CIL,而不是特定于平台的指令。

Now, recall that this is true of all .NET compilers. To illustrate, assume you created this same application using Visual Basic, rather than C#.
现在,回想一下,所有 .NET 编译器都是如此。为了说明这一点,假设您使用 Visual Basic 而不是 C# 创建了相同的应用程序。

```
REM: Calc.vb
Module Program
    REM: This class contains the app's entry point.
    Sub Main(args As String())
        Dim c As New Calc
        Dim ans As Integer = c.Add(10, 84)
        Console.WriteLine("10 + 84 is {0}", ans)
        REM: Wait for user to press the Enter key before shutting down
        Console.ReadLine()
    End Sub
End Module
REM: The VB.NET calculator.
Class Calc
    Public Function Add(ByVal addend1 As Integer, ByVal addend2 As Integer) As Integer
        Return addend1 + addend2
    End Function
End Class
```

If you examine the CIL for the Add() method, you find similar instructions (slightly tweaked by the Visual Basic compiler).
如果您检查 Add()方法的 CIL,您会发现类似的指令(Visual Basic 编译器略有调整)。

.method public instance int32 Add(int32 addend1,
                    int32 addend2) cil managed
{
// Code size     9 (0x9)
.maxstack 2
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: add.ovf
IL_0004: stloc.0
IL_0005: br.s    IL_0007
IL_0007: ldloc.0
IL_0008: ret
} // end of method Calc::Add

As a final example, the same simple Calc program developed in F# (another .NET language) is shown here:
作为最后一个示例,下面显示了用 F#(另一种 .NET 语言)开发的相同简单 Calc 程序:

// Learn more about F# at http://fsharp.org
// Calc.fs
open System

module Calc =
    let add addend1 addend2 =
        addend1 + addend2

[<EntryPoint>]
let main argv =
    let ans = Calc.add 10 84
    printfn "10 + 84 is %d" ans
    Console.ReadLine()
    0

If you examine the CIL for the Add() method, once again you find similar instructions (slightly tweaked by the F# compiler).
如果检查 Add()方法的 CIL,则会再次发现类似的指令(F# 编译器略有调整)。

.method public instance int32 Add(int32 addend1,
                    int32 addend2) cil managed
{
// Code size 9 (0x9)
.maxstack 2
.locals init (int32 V_0)
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldarg.2
IL_0003: add.ovf
IL_0004: stloc.0
IL_0005: br.s        IL_0007

IL_0007: ldloc.0
IL_0008: ret
} // end of method Calc::Add

Benefits of CIL

CIL的好处

At this point, you might be wondering exactly what is gained by compiling source code into CIL rather than directly to a specific instruction set. One benefit is language integration. As you have already seen, each .NET compiler produces nearly identical CIL instructions. Therefore, all languages are able to interact within a well-defined binary arena.
在这一点上,您可能想知道通过将源代码编译到 CIL 而不是直接编译到特定指令集会得到什么。一个好处是语言集成。如您所见,每个 .NET 编译器都会生成几乎相同的 CIL 指令。因此,所有语言都能够在明确定义的二进制领域内进行交互。

Furthermore, given that CIL is platform-agnostic, the .NET Framework itself is platform-agnostic, providing the same benefits Java developers have grown accustomed to (e.g., a single code base running on numerous operating systems). In fact, there is an international standard for the C# language. Prior to .NET Core, there were numerous implementations of the .NET framework for non-Windows platforms, such as Mono. These still exist, although the release of .NET 6 greatly reduced the need for those other platforms.
此外,鉴于CIL与平台无关,.NET Framework本身与平台无关,提供了Java开发人员已经习惯的相同好处(例如,在众多操作系统上运行的单个代码库)。事实上,C# 语言有一个国际标准。在.NET Core之前,有许多用于非Windows平台(如Mono)的.NET框架实现。这些仍然存在,尽管 .NET 6 的发布大大减少了对这些其他平台的需求。

Compiling CIL to Platform-Specific Instructions

将 CIL 编译为特定于平台的指令

Because assemblies contain CIL instructions rather than platform-specific instructions, CIL code must be compiled on the fly before use. The entity that compiles CIL code into meaningful CPU instructions is a JIT compiler, which sometimes goes by the friendly name of jitter. The .NET runtime environment leverages a JIT compiler for each CPU targeting the runtime, each optimized for the underlying platform.
由于程序集包含 CIL 指令而不是特定于平台的指令,因此必须在使用前动态编译 CIL 代码。将 CIL 代码编译为有意义的 CPU 指令的实体是 JIT 编译器,它有时使用友好名称 jitter。.NET 运行时环境为面向运行时的每个 CPU 利用一个 JIT 编译器,每个 CPU 都针对基础平台进行了优化。

For example, if you are building a .NET application to be deployed to a handheld device (such as an iOS or Android phone), the corresponding jitter is well equipped to run within a low-memory environment. On the other hand, if you are deploying your assembly to a back-end company server (where memory is seldom an issue), the jitter will be optimized to function in a high-memory environment. In this way, developers can write a single body of code that can be efficiently JIT compiled and executed on machines with different architectures.
例如,如果您正在构建要部署到手持设备(如 iOS 或 Android 手机)的 .NET 应用程序,则相应的抖动非常适合在低内存环境中运行。另一方面,如果要将程序集部署到后端公司服务器(内存很少出现问题),则抖动将经过优化,以便在高内存环境中运行。通过这种方式,开发人员可以编写单个代码体,这些代码可以在具有不同体系结构的计算机上有效地进行 JIT 编译和执行。
Furthermore, as a given jitter compiles CIL instructions into corresponding machine code, it will cache the results in memory in a manner suited to the target operating system. In this way, if a call is made to a method named PrintDocument(), the CIL instructions are compiled into platform-specific instructions on the first invocation and retained in memory for later use. Therefore, the next time PrintDocument() is called, there is no need to recompile the CIL.
此外,当给定的抖动将CIL指令编译成相应的机器代码时,它将以适合目标操作系统的方式将结果缓存在内存中。这样,如果调用名为 PrintDocument() 的方法,CIL 指令将在第一次调用时编译为特定于平台的指令,并保留在内存中供以后使用。因此,下次调用 PrintDocument() 时,无需重新编译 CIL。

Precompiling CIL to Platform-Specific Instructions

将 CIL 预编译为特定于平台的指令

There is a utility in .NET called crossgen.exe, which can be used to pre-JIT your code. Fortunately, in .NET Core 6, the ability to produce “ready-to-run” assemblies is built into the framework. More on this later in this book.
.NET 中有一个名为 crossgen.exe 的实用程序,可用于预 JIT 代码。幸运的是,在 .NET Core 6 中,生成“即用型”程序集的功能内置于框架中。本书后面会详细介绍这一点。

The Role of .NET Type Metadata.NET

类型元数据的作用

In addition to CIL instructions, a .NET assembly contains full, complete, and accurate metadata, which describes every type (e.g., class, structure, enumeration) defined in the binary, as well as the members of each type (e.g., properties, methods, events). Thankfully, it is always the job of the compiler (not the programmer) to emit the latest and greatest type metadata. Because .NET metadata is so wickedly meticulous, assemblies are completely self-describing entities.
除了 CIL 指令之外,.NET 程序集还包含完整、完整且准确的元数据,这些元数据描述二进制文件中定义的每种类型(例如,类、结构、枚举)以及每种类型的成员(例如,属性、方法、事件)。值得庆幸的是,这始终是编译器的工作(不是程序员)发出最新和最好的类型元数据。由于 .NET 元数据非常细致,因此程序集是完全自描述的实体。

To illustrate the format of .NET type metadata, let’s take a look at the metadata that has been generated for the Add() method of the C# Calc class you examined previously (the metadata generated for the Visual Basic version of the Add() method is similar, so we will examine the C# version only).
为了说明 .NET 类型元数据的格式,让我们看一下为前面检查的 C# Calc 类的 Add() 方法生成的元数据(为 Add() 方法的 Visual Basic 版本生成的元数据类似,因此我们将仅检查 C# 版本)。

// TypeDef #2 (02000003)
// -------------------------------------------------------
// TypDefName: Calc (02000003)
// Flags : [NotPublic] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit] (00100000)
// Extends : 0100000D [TypeRef] System.Object
// Method #1 (06000003)
// -------------------------------------------------------
// MethodName: Add (06000003)
// Flags : [Public] [HideBySig] [ReuseSlot] (00000086)
// RVA : 0x00002090
// ImplFlags : [IL] [Managed] (00000000)
// CallCnvntn: [DEFAULT]
// hasThis
// ReturnType: I4
// 2 Arguments
// Argument #1: I4
// Argument #2: I4
// 2 Parameters
// (1) ParamToken : (08000002) Name : addend1 flags: [none] (00000000)
// (2) ParamToken : (08000003) Name : addend2 flags: [none] (00000000)

Metadata is used by numerous aspects of the .NET runtime environment, as well as by various development tools. For example, the IntelliSense feature provided by tools such as Visual Studio is made possible by reading an assembly’s metadata at design time. Metadata is also used by various object-browsing utilities, debugging tools, and the C# compiler itself. To be sure, metadata is the backbone of numerous .NET technologies including reflection, late binding, and object serialization. Chapter 17 will formalize the role of .NET metadata.
元数据由 .NET 运行时环境的许多方面以及各种开发工具使用。例如,Visual Studio 等工具提供的智能感知功能是通过在设计时读取程序集的元数据来实现的。元数据还由各种对象浏览实用程序、调试工具和 C# 编译器本身使用。可以肯定的是,元数据是许多 .NET 技术的支柱,包括反射、后期绑定和对象序列化。第17章将正式确定.NET 元数据。

The Role of the Assembly Manifest

程序集清单的作用

Last but not least, remember that a .NET assembly also contains metadata that describes the assembly itself (technically termed a manifest). Among other details, the manifest documents all external assemblies required by the current assembly to function correctly, the assembly’s version number, copyright information, and so forth. Like type metadata, it is always the job of the compiler to generate the assembly’s manifest. Here are some relevant details of the manifest generated when compiling the Calc.cs code file shown earlier in this chapter (some lines omitted for brevity):
最后但并非最不重要的一点是,请记住,.NET 程序集还包含描述程序集本身的元数据(技术上称为清单)。除其他详细信息外,清单还记录了当前程序集正常运行所需的所有外部程序集、程序集的版本号、版权信息等。与类型元数据一样,编译器的工作始终是生成程序集的清单。以下是编译本章前面所示的 Calc.cs 代码文件时生成的清单的一些相关详细信息(为简洁起见,省略了一些行):

.assembly extern /*23000001*/ System.Runtime
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 6:0:0:0
}
.assembly extern /*23000002*/ System.Console
{
.publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) // .?_....:
.ver 6:0:0:0
}
.assembly /*20000001*/ Calc.Cs
{
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module Calc.Cs.dll
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY

In a nutshell, the manifest documents the set of external assemblies required by Calc.dll (via the .assembly extern directive) as well as various characteristics of the assembly itself (e.g., version number, module name). Chapter 16 will examine the usefulness of manifest data in much more detail.
简而言之,清单记录了 Calc 所需的外部程序集集.dll(通过.assembly extern指令)以及程序集本身的各种特征(例如,版本号,模块名称)。第16章将更详细地研究清单数据的有用性。

Understanding the Common Type System

了解通用类型系统

A given assembly may contain any number of distinct types. In the world of .NET, type is simply a general term used to refer to a member from the set {class, interface, structure, enumeration, delegate}. When you build solutions using a .NET language, you will most likely interact with many of these types. For example, your assembly might define a single class that implements some number of interfaces. Perhaps one of the interface methods takes an enumeration type as an input parameter and returns a structure to the caller.
给定程序集可能包含任意数量的不同类型。在 .NET 世界中,类型只是一个通用术语,用于引用集合 {类、接口、结构、枚举、委托} 中的成员。使用 .NET 语言生成解决方案时,很可能会与其中许多类型进行交互。例如,程序集可能定义实现一定数量接口的单个类。也许其中一个接口方法将枚举类型作为输入参数,并将结构返回给调用方。

Recall that the CTS is a formal specification that documents how types must be defined in order to be hosted by the .NET Runtime. Typically, the only individuals who are deeply concerned with the inner workings of the CTS are those building tools and/or compilers that target the .NET platform. It is important, however, for all .NET programmers to learn about how to work with the five types defined by the CTS in their language of choice. The following is a brief overview.
回想一下,CTS 是一个正式规范,它记录了必须如何定义类型才能由 .NET 运行时托管。通常,唯一深切关注内在的人CTS 的工作原理是那些面向 .NET 平台的构建工具和/或编译器。但是,对于所有 .NET 程序员来说,了解如何使用他们选择的语言中 CTS 定义的五种类型非常重要。以下是简要概述。

CTS Class TypesCTS

类类型
Every .NET language supports, at the least, the notion of a class type, which is the cornerstone of object- oriented programming (OOP). A class may be composed of any number of members (such as constructors, properties, methods, and events) and data points (fields). In C#, classes are declared using the class keyword, like so:
每种 .NET 语言至少都支持类类型的概念,这是面向对象编程 (OOP) 的基石。类可以由任意数量的成员(如构造函数、属性、方法和事件)和数据点(字段)组成。在 C# 中,类是使用 class 关键字声明的,如下所示:

```
// A C# class type with 1 method. 
// 具有 1 个方法的 C# 类类型。
class Calc
{
    public int Add(int addend1, int addend2)
    {
        return addend1 + addend2;
    }
}
```

Chapter 5 will begin your formal examination of building class types with C#; however, Table 1-1 documents a number of characteristics pertaining to class types.
第 5 章将开始对使用 C# 构建类类型进行正式检查;但是,表 1-1 记录了许多与类类型相关的特征。

Table 1-1. CTS Class Characteristics
表 1-1. CTS 类特征

Class Characteristic
类特征
Meaning in Life
意义
Is the class sealed?
类是密封的吗?
Sealed classes cannot function as a base class to other classes.
密封类不能充当其他类的基类。
Does the class implement any interfaces?
该类是否实现任何接口?
An interface is a collection of abstract members that provides a contract between the object and object user. The CTS allows a class to implement any number of interfaces.
接口是抽象成员的集合,它提供对象和对象用户之间的协定。CTS 允许一个类实现任意数量的接口。
Is the class abstract or concrete?
类是抽象的还是具体的?
Abstract classes cannot be directly instantiated but are intended to define common behaviors for derived types. Concrete classes can be instantiated directly.
抽象类不能直接实例化,但旨在定义派生类型的常见行为。具体类可以直接实例化。
What is the visibility of this class?
这个类的可见性是多少?
Each class must be configured with a visibility keyword such as public or internal. Basically, this controls whether the class may be used by external assemblies or only from within the defining assembly.
每个类都必须配置一个可见性关键字,例如公共或内部。基本上,这控制类是可以由外部程序集使用还是只能从定义程序集内部使用。

CTS Interface Types

CTS 接口类型

Interfaces are nothing more than a named collection of abstract member definitions and/or (introduced in C# 8) default implementations, which are implemented (optionally in the case of default implementations) by a given class or structure. In C#, interface types are defined using the interface keyword. By convention, all .NET interfaces begin with a capital letter I, as in the following example:
接口只不过是抽象成员定义和/或(在 C# 8 中引入)默认实现的命名集合,这些实现由给定的类或结构实现(在默认实现的情况下是可选的)。在 C# 中,接口类型是使用接口关键字定义的。按照约定,所有 .NET 接口都以大写字母 I 开头,如以下示例所示:

```
// A C# interface type is usually
// declared as public, to allow types in other
// assemblies to implement their behavior.
// C# 接口类型通常是
// 声明为公共,以允许其他类型
// 用于实现其行为的程序集。
public interface IDraw
{
    void Draw();
}
```

On their own, interfaces are of little use. However, when a class or structure implements a given interface in its unique way, you are able to request access to the supplied functionality using an interface reference in a polymorphic manner. Interface-based programming will be fully explored in Chapter 8.
就其本身而言,接口几乎没有用处。但是,当类或结构以其唯一方式实现给定接口时,您可以使用接口引用以多态方式请求访问所提供的功能。第8章将全面探讨基于接口的编程。

CTS Structure Types

CTS 结构类型

The concept of a structure is also formalized under the CTS. If you have a C background, you should be pleased to know that these user-defined types (UDTs) have survived in the world of .NET (although they behave a bit differently under the hood). Simply put, a structure can be thought of as a lightweight class type having value-based semantics. For more details on the subtleties of structures, see Chapter 4. Typically, structures are best suited for modeling geometric and mathematical data and are created in C# using the struct keyword, as follows:
结构的概念也在CTS下正式确定。如果你有 C 背景,你应该很高兴知道这些用户定义类型 (UDT) 在 .NET 世界中幸存下来(尽管它们在后台的行为略有不同)。简单地说,可以将结构视为具有基于值的语义的轻量级类类型。有关结构微妙之处的更多详细信息,请参阅第4章。通常,结构最适合对几何和数学数据进行建模,并使用 struct 关键字在 C# 中创建,如下所示:

```
// A C# structure type.
// C# 结构类型。
struct Point
{
    // Structures can contain fields.
    // 结构可以包含字段。
    public int xPos, yPos;

    // Structures can contain parameterized constructors.
    // 结构可以包含参数化构造函数。
    public Point(int x, int y)
    { xPos = x; yPos = y; }

    // Structures may define methods.
    // 结构可以定义方法。
    public void PrintPosition()
    {
        Console.WriteLine("({0}, {1})", xPos, yPos);
    }
}
```

CTS Enumeration Types

CTS枚举类型

Enumerations are a handy programming construct that allow you to group name-value pairs. For example, assume you are creating a video game application that allows the player to select from three character categories (Wizard, Fighter, or Thief). Rather than keeping track of simple numerical values to represent each possibility, you could build a strongly typed enumeration using the enum keyword.
枚举是一种方便的编程构造,可用于对名称-值对进行分组。例如,假设您正在创建一个视频游戏应用程序,该应用程序允许玩家从三个角色类别(巫师、战士或小偷)中进行选择。与其跟踪简单的数值来表示每种可能性,不如使用 enum 关键字生成强类型枚举。

```
// A C# enumeration type.
// C# 枚举类型。
enum CharacterTypeEnum
{
    Wizard = 100,
    Fighter = 200,
    Thief = 300
}
```

By default, the storage used to hold each item is a 32-bit integer; however, it is possible to alter this storage slot if need be (e.g., when programming for a low-memory device such as a mobile device). Also, the CTS demands that enumerated types derive from a common base class, System.Enum. As you will see in Chapter 4, this base class defines a number of interesting members that allow you to extract, manipulate, and transform the underlying name-value pairs programmatically.
默认情况下,用于保存每个项目的存储为 32 位整数;但是,如果需要,可以更改此存储插槽(例如,在为诸如移动设备的低内存设备编程时)。此外,CTS 要求枚举类型派生自公共基类 System.Enum。正如您将在第 4 章中看到的,此基类定义了许多有趣的成员,这些成员允许您以编程方式提取、操作和转换基础名称-值对。

CTS Delegate Types

CTS 委托类型

Delegates are the .NET equivalent of a type-safe, C-style function pointer. The key difference is that a .NET delegate is a class that derives from System.MulticastDelegate, rather than a simple pointer to a raw memory address. In C#, delegates are declared using the delegate keyword. 委托等效于类型安全的 C 样式函数指针。主要区别在于,.NET 委托是派生自 System.MulticastDelegate 的类,而不是指向原始内存地址的简单指针。在 C# 中,委托是使用 delegate 关键字声明的。

// This C# delegate type can "point to" any method
// 此 C# 委托类型可以“指向”任何方法
// returning an int and taking two ints as input.
// 返回一个 int 并将两个整数作为输入。
delegate int BinaryOp(int x, int y);

Delegates are critical when you want to provide a way for one object to forward a call to another object and provide the foundation for the .NET event architecture. As you will see in Chapters 12 and 14, delegates have intrinsic support for multicasting (i.e., forwarding a request to multiple recipients) and asynchronous method invocations (i.e., invoking the method on a secondary thread).
如果要为一个对象提供将调用转发到另一个对象并为 .NET 事件体系结构提供基础的方法,则委托至关重要。正如您将在第 12 章和第 14 章中看到的,委托对多播(即将请求转发给多个接收者)和异步方法调用(即在辅助线程上调用该方法)具有内在支持。

CTS Type Members

CTS 类型成员

Now that you have previewed each of the types formalized by the CTS, realize that most types take any number of members. Formally speaking, a type member is constrained by the set {constructor, finalizer, static constructor, nested type, operator, method, property, indexer, field, read-only field, constant, event}.
现在,您已经预览了 CTS 形式化的每个类型,请注意大多数类型采用任意数量的成员。从形式上讲,类型成员受集合 {构造函数、终结器、静态构造函数、嵌套类型、运算符、方法、属性、索引器、字段、只读字段、常量、事件} 的约束。

The CTS defines various adornments that may be associated with a given member. For example, each member has a given visibility trait (e.g., public, private, protected). Some members may be declared as abstract (to enforce a polymorphic behavior on derived types) as well as virtual (to define a canned, but overridable, implementation). Also, most members may be configured as static (bound at the class level) or instance (bound at the object level). The creation of type members is examined over the course of the next several chapters.
CTS 定义了可能与给定成员关联的各种装饰。例如,每个成员都有一个给定的可见性特征(例如,公共、私有、受保护)。某些成员可能被声明为抽象(以对派生类型强制实施多态行为)以及虚拟(以定义固定但可重写的实现)。此外,大多数成员可以配置为静态(在类级别绑定)或实例(在对象级别绑定)。类型成员的创建将在接下来的几章中进行检查。

■ Note
as described in Chapter 10, the C# language also supports the creation of generic types and generic members.
注意 如第 10 章中所述,C# 语言还支持创建泛型类型和泛型成员。

Intrinsic CTS Data Types

固有 CTS 数据类型

The final aspect of the CTS to be aware of for the time being is that it establishes a well-defined set of fundamental data types. Although a given language typically has a unique keyword used to declare a fundamental data type, all .NET language keywords ultimately resolve to the same CTS type defined in an assembly named mscorlib.dll. Consider Table 1-2, which documents how key CTS data types are expressed in VB.NET and C#.
CTS 目前需要注意的最后一个方面是它建立了一组定义明确的基本数据类型。尽管给定语言通常具有用于声明基本数据类型的唯一关键字,但所有 .NET 语言关键字最终解析为在名为 mscorlib.dll 的程序集中定义的相同 CTS 类型。请考虑表 1-2,其中记录了关键 CTS 数据类型如何在 VB.NET 和 C# 中表示。

Table 1-2. The Intrinsic CTS Data Types
表 1-2. 固有 CTS 数据类型

CTS Data Type VB Keyword C# Keyword
System.Byte Byte byte
System.SByte SByte sbyte
System.Int16 Short short
System.Int32 Integer int
System.Int64 Long long
System.UInt16 UShort ushort
System.UInt32 UInteger uint
System.UInt64 ULong ulong
System.Single Single float
System.Double Double double
System.Object Object object
System.Char Char char
System.String String string
System.Decimal Decimal decimal
System.Boolean Boolean bool

Given that the unique keywords of a managed language are simply shorthand notations for a real type in the System namespace, you no longer have to worry about overflow/underflow conditions for numerical data or how strings and Booleans are internally represented across different languages. Consider the following code snippets, which define 32-bit numerical variables in C# and Visual Basic, using language keywords as well as the formal CTS data type:
鉴于托管语言的唯一关键字只是 System 命名空间中实际类型的简写表示法,您不再需要担心数值数据的溢出/下溢情况,也不必担心字符串和布尔值在不同语言中的内部表示方式。请考虑以下代码片段,这些代码片段使用语言关键字和正式的 CTS 数据类型在 C# 和 Visual Basic 中定义 32 位数值变量:

```
// Define some "ints" in C#.
// 在 C# 中定义一些“整数”。
int i = 0; 
System.Int32 j = 0;

REM: Define some "ints" in VB.
REM: 在 VB 中定义一些“整数”。
Dim i As Integer = 0
Dim j As System.Int32 = 0
```

Understanding the Common Language Specification

了解公共语言规范

As you are aware, different languages express the same programming constructs in unique, language- specific terms. For example, in C# you denote string concatenation using the plus operator (+), while in VB you typically make use of the ampersand (&). Even when two distinct languages express the same programmatic idiom (e.g., a function with no return value), the chances are good that the syntax will appear quite different on the surface.
如您所知,不同的语言以独特的、特定于语言的术语表达相同的编程结构。例如,在 C# 中,使用加号运算符 (+) 表示字符串连接,而在 VB 中,通常使用与号 (&)。即使两种不同的语言表达相同编程习语(例如,没有返回值的函数),语法很有可能在表面上看起来完全不同。

```
// C# method returning nothing.
// 方法不返回任何内容。
public void MyMethod()
{
    // Some interesting code...
    // 一些有趣的代码...
}
```

```
REM: VB method returning nothing.
REM: VB 方法不返回任何内容。
Public Sub MyMethod()
    REM: Some interesting code...
    REM: 一些有趣的代码...
End Sub
```

As you have already seen, these minor syntactic variations are inconsequential in the eyes of the .NET runtime, given that the respective compilers (csc.exe or vbc.exe, in this case) emit a similar set of CIL instructions. However, languages can also differ with regard to their overall level of functionality. For
example, a .NET language might or might not have a keyword to represent unsigned data and might or might not support pointer types. Given these possible variations, it would be ideal to have a baseline to which all.NET languages are expected to conform.
正如你已经看到的,这些微小的句法变化在.NET 运行时,假设相应的编译器(在本例中为 csc.exe 或 vbc.exe)发出一组类似的 CIL 指令。但是,语言在整体功能级别方面也可能有所不同。例如,.NET 语言可能有也可能没有用于表示无符号数据的关键字,并且可能支持也可能不支持指针类型。鉴于这些可能的变化,理想的做法是有一个基线,所有.NET 语言应符合要求。

The CLS is a set of rules that describe in vivid detail the minimal and complete set of features a given.NET compiler must support to produce code that can be hosted by the .NET Runtime, while at the same time be accessed in a uniform manner by all languages that target the .NET platform. In many ways, the CLS can be viewed as a subset of the full functionality defined by the CTS.
CLS 是一组规则,用于生动详细地描述给定的最小和完整功能集.NET 编译器必须支持生成可由 .NET 运行时承载的代码,同时由面向 .NET 平台的所有语言以统一方式访问。在许多方面,CLS 可以被视为 CTS 定义的完整功能的子集。

The CLS is ultimately a set of rules that compiler builders must conform to if they intend their products to function seamlessly within the .NET universe. Each rule is assigned a simple name (e.g., CLS Rule 6) and describes how this rule affects those who build the compilers as well as those who (in some way) interact with them. The crème de la crème of the CLS is Rule 1. CLS
最终是一组规则,如果编译器构建者希望其产品在 .NET 世界中无缝运行,则必须遵守这些规则。每个规则都被分配了一个简单的名称(例如,CLS 规则 6),并描述了该规则如何影响构建编译器的人以及(以某种方式)与他们交互的人。CLS的精华是规则1。

_ Rule 1: CLS rules apply only to those parts of a type that are exposed outside the defining assembly.
_ 规则 1:CLS 规则仅适用于在定义程序集外部公开的类型零件。

Given this rule, you can (correctly) infer that the remaining rules of the CLS do not apply to the logic used to build the inner workings of a .NET type. The only aspects of a type that must conform to the CLS are the member definitions themselves (i.e., naming conventions, parameters, and return types). The

implementation logic for a member may use any number of non-CLS techniques, as the outside world won’t know the difference.
给定此规则,您可以(正确)推断 CLS 的其余规则不适用于用于生成 .NET 类型内部工作的逻辑。类型中唯一必须符合 CLS 的方面是成员定义本身(即命名约定、参数和返回类型)。这成员的实现逻辑可以使用任意数量的非 CLS 技术,因为外界不会知道其中的区别。

To illustrate, the following C# Add() method is not CLS compliant, as the parameters and return values make use of unsigned data (which is not a requirement of the CLS):
为了说明这一点,以下 C# Add() 方法不符合 CLS,因为参数和返回值使用未签名的数据(这不是 CLS 的要求):

class Calc
{
    // Exposed unsigned data is not CLS compliant!
    // 暴露的未签名数据不符合 CLS 标准!
    public ulong Add(ulong addend1, ulong addend2)
    {
        return addend1 + addend2;
    }
}

However, consider the following code that makes use of unsigned data internally in a method:
但是,请考虑以下代码,该代码在方法内部使用未签名的数据:

class Calc
{
    public int Add(int addend1, int addend2)
    {
        // As this ulong variable is only used internally,
        // 由于此 ulong 变量仅在内部使用,
        // we are still CLS compliant.
        // 我们仍然符合CLS标准。
        ulong temp = 0;
        ......
        return addend1 + addend2;
    }
}

The class still conforms to the rules of the CLS and can rest assured that all .NET languages are able to invoke the Add() method.
该类仍然符合 CLS 的规则,并且可以放心,所有 .NET 语言都能够调用 Add() 方法。

Of course, in addition to Rule 1, the CLS defines numerous other rules. For example, the CLS describes how a given language must represent text strings, how enumerations should be represented internally (the base type used for storage), how to define static members, and so forth. Luckily, you don’t have to commit these rules to memory to be a proficient .NET developer. Again, by and large, an intimate understanding of the CTS and CLS specifications is typically of interest only to tool/compiler builders.
当然,除了规则 1 之外,CLS 还定义了许多其他规则。例如,CLS 描述给定语言必须如何表示文本字符串、枚举应在内部表示(用于存储的基类型)、如何定义静态成员等。幸运的是,您不必将这些规则提交到内存中即可成为熟练的 .NET 开发人员。同样,总的来说,对 CTS 和 CLS 规范的深入了解通常只有工具/编译器构建者感兴趣。

Ensuring CLS Compliance

确保 CLS 合规性

As you will see over the course of this book, C# does define a number of programming constructs that are not CLS compliant. The good news, however, is that you can instruct the C# compiler to check your code for CLS compliance using a single .NET attribute.
正如您将在本书中看到的那样,C# 确实定义了许多不符合 CLS 的编程结构。不过,好消息是,您可以指示 C# 编译器使用单个 .NET 属性检查代码是否符合 CLS。

// Tell the C# compiler to check for CLS compliance. 
// 告诉 C# 编译器检查 CLS 合规性。
[assembly: CLSCompliant(true)]

Chapter 17 dives into the details of attribute-based programming. Until then, simply understand that the [CLSCompliant] attribute will instruct the C# compiler to check every line of code against the rulesof the CLS. If any CLS violations are discovered, you receive a compiler warning and a description of the offending code.
第17章深入探讨了基于属性的编程的细节。在此之前,只需了解[CLSCompliant] 属性将指示 C# 编译器根据规则检查每一行代码的 CLS。如果发现任何 CLS 冲突,您将收到编译器警告和违规代码的说明。

Understanding the .NET Runtime

了解 .NET 运行时

In addition to the CTS and CLS specifications, the final piece of the puzzle to contend with is the .NET Runtime. Programmatically speaking, the term runtime can be understood as a collection of services that are required to execute a given compiled unit of code. For example, when Java developers deploy software to a new computer, they need to ensure the Java virtual machine (JVM) has been installed on the machine in order to run their software.
除了 CTS 和 CLS 规范之外,要解决的最后一块难题是 .NET 运行时。从编程方式讲,术语运行时可以理解为执行给定的已编译代码单元所需的服务集合。例如,当 Java 开发人员将软件部署到新计算机时,他们需要确保已在计算机上安装 Java 虚拟机 (JVM) 才能运行其软件。

The .NET platform offers yet another runtime system. The key difference between the .NET runtime and the various other runtimes I just mentioned is that the .NET runtime provides a single, well-defined runtime layer that is shared by all languages and platforms that are .NET.
.NET 平台提供了另一个运行时系统。.NET 运行时与我刚才提到的各种其他运行时之间的主要区别在于,.NET 运行时提供了一个由 .NET 的所有语言和平台共享的单个定义明确的运行时层。

Distinguishing Between Assembly, Namespace, and Type

区分程序集、命名空间和类型

Each of us understands the importance of code libraries. The point of framework libraries is to give developers a well-defined set of existing code to leverage in their applications. However, the C# language does not come with a language-specific code library. Rather, C# developers leverage the language-neutral.NET libraries. To keep all the types within the base class libraries well organized, the .NET platform makes extensive use of the namespace concept.
我们每个人都明白代码库的重要性。框架库的要点是为开发人员提供一组定义良好的现有代码,以便在其应用程序中利用。但是,C# 语言不附带特定于语言的代码库。相反,C# 开发人员利用与语言无关.NET 库。为了使基类库中的所有类型井井有条,.NET 平台广泛使用命名空间概念。

A namespace is a grouping of semantically related types contained in an assembly or possibly spread across multiple related assemblies. For example, the System.IO namespace contains file I/O-related types, the System.Data namespace defines basic database types, and so on. It is important to point out that a single assembly can contain any number of namespaces, each of which can contain any number of types.
命名空间是程序集中包含的语义相关类型的分组,或者可能分布在多个相关程序集中。例如,System.IO 命名空间包含与文件 I/O 相关的类型,System.Data 命名空间定义基本数据库类型,等等。必须指出的是,单个程序集可以包含任意数量的命名空间,每个命名空间可以包含任意数量的类型。

The key difference between this approach and a language-specific library is that any language targeting the .NET runtime uses the same namespaces and same types. For example, the following two programs all illustrate the ubiquitous Hello World application, written in C# and VB:
此方法与特定于语言的库之间的主要区别在于,面向 .NET 运行时的任何语言都使用相同的命名空间和相同类型。例如,以下两个程序都说明了用 C# 和 VB 编写的无处不在的 Hello World 应用程序:

■ Note
The following code uses the C# 9 version of the Program class with a void Main() method to help illustrate the example. The new templates in C# 10 use top level statements (covered in Chapter 3) and global implicit using statements (covered later in this chapter).
下面的代码使用带有 void Main() 方法的 C# 9 版本的 Program 类来帮助说明该示例。C# 10 中的新模板使用顶级语句(在第 3 章中介绍)和全局隐式 using 语句(在本章后面介绍)。

```
// Hello World in C#.
using System;
public class MyApp
{
    static void Main()
    {
        Console.WriteLine("Hi from C#");
    }
}
```

```
REM: Hello World in VB.
Imports System
Public Module MyApp
    Sub Main()
        Console.WriteLine("Hi from VB")
    End Sub
End Module
```

Notice that each language is using the Console class defined in the System namespace. Beyond some obvious syntactic variations, these applications look and feel very much alike, both physically and logically.
请注意,每种语言都使用 System 命名空间中定义的 Console 类。除了一些明显的语法变化之外,这些应用程序在物理和逻辑上的外观和感觉都非常相似。

Clearly, once you are comfortable with your .NET programming language of choice, your next goal as a .NET developer is to get to know the wealth of types defined in the (numerous) .NET namespaces. The most fundamental namespace to get your head around initially is named System. This namespace provides a core body of types that you will need to leverage time and again as a .NET developer. In fact, you cannot build any sort of functional C# application without at least making a reference to the System namespace, as the core data types (e.g., System.Int32, System.String) are defined here. Table 1-3 offers a rundown of some (but certainly not all) of the .NET namespaces grouped by related functionality.
显然,一旦你对你选择的.NET编程语言感到满意,你的下一个目标就是.NET 开发人员将了解(大量).NET 命名空间中定义的大量类型。最初要了解的最基本的命名空间名为 System。此命名空间提供了作为 .NET 开发人员需要一次又一次利用的核心类型主体。事实上,如果不至少引用 System 命名空间,就无法生成任何类型的功能性 C# 应用程序,因为此处定义了核心数据类型(例如,System.Int32、System.String)。表 1-3 提供了按相关功能分组的一些(但肯定不是全部).NET 命名空间的概要。

Table 1-3. A Sampling of .NET Namespaces
表 1-3. .NET 命名空间的示例

.NET Namespace
.NET 命名空间
Meaning in Life
意义
System Within System, you find numerous useful types dealing with intrinsic data, mathematical computations, random number generation, environment variables, and garbage collection, as well as a number of commonly used exceptions and attributes.
在 System 中,您会发现许多有用的类型,这些类型处理内部数据、数学计算、随机数生成、环境变量和垃圾回收,以及许多常用的异常和属性。
System.Collections
System.Collections.Generic
These namespaces define a number of stock container types, as well as base types and interfaces that allow you to build customized collections.
这些命名空间定义了许多常用容器类型,以及允许您生成自定义集合的基类型和接口。
System.Data System.Data.Common
System.Data.SqlClient
These namespaces are used for interacting with relational databases using ADO.NET.
这些命名空间用于使用 ADO.NET 与关系数据库进行交互。

System.IO System.IO.Compression
System.IO.Ports
These namespaces define numerous types used to work with file I/O, compression of data, and port manipulation.
这些命名空间定义了用于处理文件 I/O、数据压缩和端口操作的多种类型。
System.Runtime.InteropServices This namespace provides facilities to allow .NET types to interact with unmanaged code (e.g., C-based DLLs and COM servers) and vice versa.
此命名空间提供的工具允许 .NET 类型与非托管代码(例如,基于 C 的 DLL 和 COM 服务器)交互,反之亦然。
System.Drawing System.Windows.Forms These namespaces define types used to build desktop applications using .NET’s original UI toolkit (Windows Forms).
这些命名空间定义用于使用 构建桌面应用程序的类型。NET的原始UI工具包(Windows Forms)。
System.Windows System.Windows.Controls
System.Windows.Shapes
The System.Windows namespace is the root for several namespaces that are used in Windows Presentation Foundation applications.
System.Windows 命名空间是 Windows Presentation Foundation 应用程序中使用的多个命名空间的根。
System.Windows.Forms
System.Drawing
The System.Windows.Forms namespace is the root for several namespaces used in Windows Forms applications.
命名空间是 Windows 窗体应用程序中使用的多个命名空间的根。
System.Linq System.Linq.Expressions These namespaces define types used when programming against the LINQ API.
这些命名空间定义针对 LINQ API 进行编程时使用的类型。
System.AspNetCore This is one of many namespaces that allows you to build ASP.NET Core web applications and RESTful services.
这是允许您构建 ASP.NET 核心 Web 应用程序和 RESTful 服务的众多命名空间之一。
System.Threading
System.Threading.Tasks
These namespaces define numerous types to build multithreaded applications that can distribute workloads across multiple CPUs.
这些命名空间定义了多种类型来构建可在多个 CPU 之间分配工作负载的多线程应用程序。
System.Security Security is an integrated aspect of the .NET universe. In the security-centric namespaces, you find numerous types dealing with permissions, cryptography, etc.
安全性是 .NET 领域的一个集成方面。在以安全为中心的命名空间,您会发现许多处理权限、加密等的类型。
System.Xml The XML-centric namespaces contain numerous types used to interact with XML data.
以 XML 为中心的命名空间包含许多用于与 XML 数据交互的类型。

Accessing a Namespace Programmatically

以编程方式访问命名空间

It is worth reiterating that a namespace is nothing more than a convenient way for us mere humans to logically understand and organize related types. Consider again the System namespace. From your perspective, you can assume that System.Console represents a class named Console that is contained within a namespace called System. However, in the eyes of the .NET runtime, this is not so. The runtime engine sees only a single class named System.Console.
值得重申的是,命名空间只不过是我们人类在逻辑上理解和组织相关类型的一种便捷方式。再次考虑系统命名空间。从您的透视,您可以假设 System.Console 表示一个名为 Console 的类,该类包含在名为 System 的命名空间中。但是,在 .NET 运行时看来,事实并非如此。运行时引擎只看到一个名为 System.Console 的类。

In C#, the using keyword simplifies the process of referencing types defined in a particular namespace. Here is how it works. Returning to the Calc example program earlier in this chapter, there is a single using statement at the top of the file.
在 C# 中,using 关键字简化了引用特定命名空间中定义的类型的过程。这是它的工作原理。回到本章前面的 Calc 示例程序,文件顶部有一个 using 语句。

using System;

That statement is a shortcut to enable this line of code:
该语句是启用此代码行的快捷方式:

Console.WriteLine("10 + 84 is {0}.", ans);

Without the using statement, the code would need to be written like this:
如果没有 using 语句,代码需要像这样编写:

System.Console.WriteLine("10 + 84 is {0}.", ans);

While defining a type using the fully qualified name provides greater readability, I think you’d agree that the C# using keyword reduces keystrokes. In this text, we will avoid the use of fully qualified names (unless there is a definite ambiguity to be resolved) and opt for the simplified approach of the C# using keyword.
虽然使用完全限定名称定义类型提供了更高的可读性,但我认为您会同意 C# using 关键字可以减少击键。在本文中,我们将避免使用完全限定的名称(除非有明确的歧义需要解决),并选择 C# using 关键字的简化方法。

However, always remember that the using keyword is simply a shorthand notation for specifying a type’s fully qualified name, and either approach results in the same underlying CIL (given that CIL code always uses fully qualified names) and has no effect on performance or the size of the assembly.
但是,请始终记住,using 关键字只是用于指定类型的完全限定名称的简写表示法,并且这两种方法都会导致相同的基础 CIL(假设 CIL 代码始终使用完全限定名称),并且对性能或程序集的大小没有影响。

Global Using Statements (New 10.0)

全局Using语句(新 10.0)

As you build more complex C# applications, you will most likely have namespaces repeated in multiple files. Introduced in C# 10, namespaces can be referenced globally, and then be available in every file in the project automatically. Simply add the global keyword in front of your using statements, like this:
在生成更复杂的 C# 应用程序时,很可能会在多个文件中重复命名空间。在 C# 10 中引入,命名空间可以全局引用,然后自动在项目中的每个文件中可用。只需在 using 语句前面添加 global 关键字,如下所示:

global using System;

■ Note
all global using statements must come before any non-global using statements.
所有全局 using 语句都必须位于任何非全局 using 语句之前。

A recommendation is that you place the global using statements along with your top level statements (covered in Chapter 3) or a completely separate file (such as GlobalUsings.cs) for better visibility. You will see many examples of this throughout this text.
建议将全局 using 语句与顶级语句(在第 3 章中介绍)或完全独立的文件(如 GlobalUsings.cs)一起放置,以提高可见性。在本文中,您将看到许多示例。

In addition to placing the global using statements in Program.cs (or a separate file), they can be placed in the project file for the application using the following format:
除了将全局 using 语句放在 Program.cs(或单独的文件)中之外,还可以使用以下格式将它们放置在应用程序的项目文件中:

<ItemGroup>
    <Using Include=”System.Text” />
    <Using Include=”System.Text.Encodings.Web” />
    <Using Include=”System.Text.Json” />
    <Using Include=”System.Text.Json.Serialization” />
</ItemGroup>

Implicit Global Using Statements (New 10.0)

隐式全局Using语句(新 10.0)

Another new feature included with .NET 6/C# 10 are implicit global using statements. The implicit global using statements supplied by .NET 6 varies based on the type of application you are building. Table 1-4 lists the types of applications and the included namespaces.
.NET 6/C# 10 中包含的另一个新功能是隐式全局 using 语句。.NET 6 提供的隐式全局 using 语句因要生成的应用程序类型而异。表 1-4 列出了应用程序的类型和包含的命名空间。

Table 1-4. A Sampling of .NET Namespaces
表 1-4. .NET 命名空间的示例

.NET Application Type
.NET 应用程序类型
Namespaces covered by implicit global using statements
隐式全局 using 语句涵盖的命名空间
Client (Microsoft.NET.Sdk) System
System.Collections.Generic
System.IO
System.Linq
System.Net.Http
System.Threading
System.Threading.Tasks
Web (Microsoft.NET.Sdk.Web) All from Microsoft.NET.Sdk plus:
System.Net.Http.Json
Microsoft.AspNetCore.Builder
Microsoft.AspNetCore.Hosting
Microsoft.AspNetCore.Http
Microsoft.AspNetCore.Routing
Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging
Worker Service (Microsoft.NET.Sdk.Worker) All from Microsoft.NET.Sdk plus:
Microsoft.Extensions.Configuration
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging

The vast majority of the C# 10 project templates enable global implicit using statements by default with the ImplicitUsings element in the project’s main Property group. To disable the setting, update the project file to the following:
默认情况下,绝大多数 C# 10 项目模板使用项目主属性组中的 ImplicitUsings 元素启用全局隐式 using 语句。若要禁用该设置,请将项目文件更新为以下内容:

<PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>disable</ImplicitUsings>
</PropertyGroup>

To see the global using statements in your project, look for the .GlobalUsings.g.cs file in the \obj\Debug\net6.0 folder. For the Calc.cs project, the following is the generated code:
若要查看项目中的全局 using 语句,请查找 。GlobalUsings.g.cs 文件在 \obj\Debug\net6.0 文件夹中。对于 Calc.cs 项目,以下是生成的代码:

// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

File Scoped Namespaces (New 10.0)

文件范围的命名空间(新版 10.0)

Also new in C# 10, file-scoped namespaces remove the need to wrap your code in braces when placing it in a custom namespace. Take the following example of the Calculator class, contained in the CalculatorExamples namespace. Prior to C# 10, to place a class in a namespace required the namespace declaration, an opening curly brace, the code (Calculator), and then a closing curly brace. In the example, the extra code is in bold:
文件范围的命名空间也是 C# 10 中的新增功能,在将代码放入自定义命名空间时,无需将代码包装在大括号中。以以下计算器类为例,该类包含在计算器示例命名空间。在 C# 10 之前,若要将类放在命名空间中,需要命名空间声明、左大括号、代码(计算器),然后是右大括号。在示例中,额外代码以粗体显示:

namespace CalculatorExamples
{
    class Calculator()
    {

    }
}

As your code becomes more complex, this can add a lot of extra code and indentation. With file scoped namespaces, the following code achieves the same effect:
随着代码变得更加复杂,这可能会添加大量额外的代码和缩进。对于文件范围的命名空间,以下代码可实现相同的效果:

namespace CalculatorExamples
class Calculator()
{
...
}

■ Note
Custom namespaces are covered in depth in Chapter 16
自定义命名空间在第 16 章中有深入介绍

Referencing External Assemblies

引用外部程序集

Prior versions of the .NET Framework used a common installation location for framework libraries known as the Global Assembly Cache (GAC). Instead of having a single installation location, .NET does not use the GAC. Instead, each version (including minor releases) is installed in its own location (by version) on the computer. When using Windows, each version of the runtime and SDK gets installed into c:\Program Files\dotnet.
早期版本的 .NET Framework 使用框架库的通用安装位置,称为全局程序集缓存 (GAC)。.NET 不使用 GAC,而不是只有一个安装位置。相反,每个版本(包括次要版本)都安装在计算机上自己的位置(按版本)。使用 Windows 时,每个版本的运行时和 SDK 都会安装到 c:\Program Files\dotnet 中。

Adding assemblies into most .NET projects is done by adding NuGet packages (covered later in this text). However, .NET applications targeting (and being developed on) Windows still have access to COM libraries.
将程序集添加到大多数 .NET 项目中是通过添加 NuGet 包来完成的(本文稍后将介绍)。但是,面向 Windows(并在 Windows 上开发)的 .NET 应用程序仍然可以访问 COM 库。

For an assembly to have access to another assembly that you are building (or someone built for you), you need to add a reference from your assembly to the other assembly and have physical access to the other assembly. Depending on the development tool you are using to build your .NET applications, you will have various ways to inform the compiler which assemblies you want to include during the compilation cycle.
要使程序集能够访问您正在生成的另一个程序集(或为您构建的人),您需要将程序集中的引用添加到另一个程序集,并具有对其他程序集的物理访问权限。根据用于生成 .NET 应用程序的开发工具,您将有多种方法通知编译器要在编译周期中包含哪些程序集。

Exploring an Assembly Using ildasm.exe

使用 ildasm 探索程序集.exe

If you are beginning to feel a tad overwhelmed at the thought of gaining mastery over every namespace in the .NET platform, just remember that what makes a namespace unique is that it contains types that are somehow semantically related. Therefore, if you have no need for a user interface beyond a simple console application, you can forget all about the desktop and web namespaces (among others). If you are building a painting application, the database namespaces are most likely of little concern. You will learn over time the namespaces that are most relevant to your programming needs.
如果你开始对掌握 .NET 平台中的每个命名空间的想法感到有点不知所措,请记住,命名空间的独特之处在于它包含某种语义相关的类型。因此,如果您不需要简单的控制台应用程序之外的用户界面,则可以忘记有关桌面和Web命名空间(以及其他)的所有内容。如果您正在构建绘画应用程序,则数据库命名空间很可能无关紧要。随着时间的推移,您将了解与您的编程需求最相关的命名空间。

The Intermediate Language Disassembler utility (ildasm.exe) allows you to create a text document representing a .NET assembly and investigate its contents, including the associated manifest, CIL code, and type metadata. This tool allows you to dive deeply into how the C# code maps to CIL and ultimately helps you understand the inner workings of the .NET platform. While you never need to use ildasm.exe to become a proficient .NET programmer, I highly recommend you fire up this tool from time to time to better understand how your C# code maps to runtime concepts.
中间语言反汇编程序实用工具 (ildasm.exe) 允许您创建表示 .NET 程序集的文本文档并调查其内容,包括关联的清单、CIL 代码和类型元数据。此工具允许您深入了解 C# 代码如何映射到 CIL,并最终帮助您了解 .NET 平台的内部工作原理。虽然你永远不需要使用 ildasm.exe 成为一名熟练的 .NET 程序员,但我强烈建议你不时启动此工具,以更好地了解 C# 代码如何映射到运行时概念。

■ Note
The ildasm.exe program no longer ships with the .NET 6 runtime. There are two options for getting this tool into your workspace. The first is to compile from the .NET 6 runtime source located at https:// github.com/dotnet/runtime. The second, and easier method, is to pull down the desired version from www.nuget.org. ILdasm on Nuget is at https://www.nuget.org/packages/Microsoft.NETCore. ILDAsm/. Make sure to select the correct version (for this book you will want version 6.0.0 or higher). add the ILdasm Nuget package to your project with the following command: dotnet add package Microsoft. NETCore.ILDAsm –version 6.0.0.
ildasm.exe 程序不再附带 .NET 6 运行时。有两个选项可用于将此工具放入工作区。第一种是从位于 github.com/dotnet/runtime 的 .NET 6 运行时源编译 https://。第二种也是更简单的方法是从 www.nuget.org 下拉所需的版本。Nuget上的ILdasm正在 https://www.nuget.org/packages/Microsoft.NETCore。 伊尔达/.确保选择正确的版本(对于本书,您需要6.0.0或更高版本)。使用以下命令将 ILdasm Nuget 包添加到项目中:dotnet 添加包Microsoft。NETCore.ILDAsm –version 6.0.0.
This doesn’t actually load ILDasm.exe into your project but places it in your package folder (on Windows):%userprofile%.nuget\packages\microsoft.netcore.ildasm\6.0.0\runtimes\native.
这实际上不会将 ILDasm.exe 加载到您的项目中,而是将其放在您的包文件夹中(在 Windows 上):%userprofile%.nuget\packages\microsoft.netcore.ildasm\6.0.0\runtimes\native.
I have also included the 6.0.0 version of ILDasm.exe in this book’s github repo in the root folder for the code samples.
我还在本书的 github 存储库中包含了 ILDasm 的 6.0.0 版本.exe在代码示例的根文件夹中。

After you get ildasm.exe loaded onto your machine, you can run the program from the command line without any arguments to see the help comments. At a minimum, you have to specify the assembly to extract the CIL.
将 ildasm.exe 加载到计算机上后,可以从命令行运行程序,而无需任何参数来查看帮助注释。至少必须指定程序集以提取 CIL。

An example command line is as follows:
命令行示例如下:

ildasm /all /METADATA /out=csharp.il calc.cs.dll

This will create a file named csharp.il exporting all available data into the file. This is the file where the previous IL examples came from.
这将创建一个名为 csharp.il 的文件,将所有可用数据导出到文件中。这是前面的 IL 示例的来源文件。

Summary

总结

The point of this chapter was to lay out the conceptual framework necessary for the remainder of this book. I began by examining a number of limitations and complexities found within the technologies prior to .NET and followed up with an overview of how .NET and C# attempt to simplify the current state of affairs.
本章的要点是阐述本书其余部分所需的概念框架。我首先检查了 .NET 之前的技术中的一些限制和复杂性,然后概述了 .NET 和 C# 如何尝试简化当前事务状态。

.NET basically boils down to a runtime execution engine (the .NET Runtime) and base class libraries.The runtime is able to host any .NET binary (aka assembly) that abides by the rules of managed code. As you saw, assemblies contain CIL instructions (in addition to type metadata and the assembly manifest) that are compiled to platform-specific instructions using a just-in-time compiler. In addition, you explored the role of the Common Language Specification and Common Type System.
.NET 基本上归结为运行时执行引擎(.NET 运行时)和基类库。运行时能够承载遵守托管代码规则的任何 .NET 二进制文件(也称为程序集)。如您所见,程序集包含 CIL 指令(除了类型元数据和程序集清单之外),这些指令使用实时编译器编译为特定于平台的指令。此外,您还探讨了公共语言规范和公共类型系统的作用。

In the next chapter, you will take a tour of the common integrated development environments you can use when you build your C# programming projects. You will be happy to know that in this book you will use completely free (and feature-rich) IDEs, so you can start exploring the .NET universe with no money down.
在下一章中,您将浏览在生成 C# 编程项目时可以使用的常见集成开发环境。你会很高兴地知道,在本书中,你将使用完全免费(且功能丰富)的IDE,这样你就可以开始探索.NET世界了,不用花钱。

发表评论