C# 7.3 中的新增功能What's new in C# 7.3

C# 7.3 版本有两个主要主题。There are two main themes to the C# 7.3 release. 第一个主题提供使安全代码的性能与不安全代码的性能一样好的功能。One theme provides features that enable safe code to be as performant as unsafe code. 第二个主题提供对现有功能的增量改进。The second theme provides incremental improvements to existing features. 此外,在此版本中添加了新的编译器选项。In addition, new compiler options were added in this release.

以下新增功能支持使安全代码获得更好的性能的主题:The following new features support the theme of better performance for safe code:

  • 无需固定即可访问固定的字段。You can access fixed fields without pinning.
  • 可以重新分配 ref 本地变量。You can reassign ref local variables.
  • 可以使用 stackalloc 数组上的初始值设定项。You can use initializers on stackalloc arrays.
  • 可以对支持模式的任何类型使用 fixed 语句。You can use fixed statements with any type that supports a pattern.
  • 可以使用其他泛型约束。You can use additional generic constraints.

对现有功能进行了以下增强:The following enhancements were made to existing features:

  • 可以使用元组类型测试 ==!=You can test == and != with tuple types.
  • 可以在多个位置使用表达式变量。You can use expression variables in more locations.
  • 可以将属性附加到自动实现的属性的支持字段。You may attach attributes to the backing field of auto-implemented properties.
  • in 区分的参数的方法解析得到了改进。Method resolution when arguments differ by in has been improved.
  • 重载解析的多义情况现在变得更少。Overload resolution now has fewer ambiguous cases.

新的编译器选项为:The new compiler options are:

  • -publicsign,用于启用程序集的开放源代码软件 (OSS) 签名。-publicsign to enable Open Source Software (OSS) signing of assemblies.
  • -pathmap用于提供源目录的映射。-pathmap to provide a mapping for source directories.

本文的剩余部分提供了详细信息和链接,以便你详细了解每项改进。The remainder of this article provides details and links to learn more about each of the improvements. 可以使用 dotnet try 全局工具在环境中浏览这些功能:You can explore these features in your environment using the dotnet try global tool:

  1. 安装 dotnet-try 全局工具。Install the dotnet-try global tool.
  2. 克隆 dotnet/try-samples 存储库。Clone the dotnet/try-samples repository.
  3. 将当前目录设置为 try-samples 存储库的 csharp7 子目录 。Set the current directory to the csharp7 subdirectory for the try-samples repository.
  4. 运行 dotnet tryRun dotnet try.

启用更高效的安全代码Enabling more efficient safe code

你应能够安全地编写性能与不安全代码一样好的 C# 代码。You should be able to write C# code safely that performs as well as unsafe code. 安全代码可避免错误类,例如缓冲区溢出、杂散指针和其他内存访问错误。Safe code avoids classes of errors, such as buffer overruns, stray pointers, and other memory access errors. 这些新功能扩展了可验证安全代码的功能。These new features expand the capabilities of verifiable safe code. 努力使用安全结构编写更多代码。Strive to write more of your code using safe constructs. 这些功能使其更容易实现。These features make that easier.

索引 fixed 字段不需要进行固定Indexing fixed fields does not require pinning

请考虑此结构:Consider this struct:

unsafe struct S
{
    public fixed int myFixedField[10];
}

在早期版本的 C# 中,需要固定变量才能访问属于 myFixedField 的整数之一。In earlier versions of C#, you needed to pin a variable to access one of the integers that are part of myFixedField. 现在,以下代码进行编译,而不将变量 p 固定到单独的 fixed 语句中:Now, the following code compiles without pinning the variable p inside a separate fixed statement:

class C
{
    static S s = new S();

    unsafe public void M()
    {
        int p = s.myFixedField[5];
    }
}

变量 p 访问 myFixedField 中的一个元素。The variable p accesses one element in myFixedField. 无需声明单独的 int* 变量。You don't need to declare a separate int* variable. 请注意,你仍然需要 unsafe 上下文。Note that you still need an unsafe context. 在早期版本的 C# 中,需要声明第二个固定的指针:In earlier versions of C#, you need to declare a second fixed pointer:

class C
{
    static S s = new S();

    unsafe public void M()
    {
        fixed (int* ptr = s.myFixedField)
        {
            int p = ptr[5];
        }
    }
}

有关详细信息,请参阅有关 fixed 语句的文章。For more information, see the article on the fixed statement.

可能会重新分配 ref 局部变量ref local variables may be reassigned

现在,在对 ref 局部变量进行初始化后,可能会对其重新分配,以引用不同的实例。Now, ref locals may be reassigned to refer to different instances after being initialized. 以下代码现在编译:The following code now compiles:

ref VeryLargeStruct refLocal = ref veryLargeStruct; // initialization
refLocal = ref anotherVeryLargeStruct; // reassigned, refLocal refers to different storage.

有关详细信息,请参阅有关 ref 返回和 ref 局部变量以及 foreach 的文章。For more information, see the article on ref returns and ref locals, and the article on foreach.

stackalloc 数组支持初始值设定项stackalloc arrays support initializers

当你对数组中的元素的值进行初始值设定时,你已能够指定该值:You've been able to specify the values for elements in an array when you initialize it:

var arr = new int[3] {1, 2, 3};
var arr2 = new int[] {1, 2, 3};

现在,可向使用 stackalloc 进行声明的数组应用同一语法:Now, that same syntax can be applied to arrays that are declared with stackalloc:

int* pArr = stackalloc int[3] {1, 2, 3};
int* pArr2 = stackalloc int[] {1, 2, 3};
Span<int> arr = stackalloc [] {1, 2, 3};

有关详细信息,请参阅stackalloc运算符一文。For more information, see the stackalloc operator article.

更多类型支持 fixed 语句More types support the fixed statement

fixed 语句支持有限的一组类型。The fixed statement supported a limited set of types. 从 C# 7.3 开始,任何包含返回 ref Tref readonly TGetPinnableReference() 方法的类型均有可能为 fixedStarting with C# 7.3, any type that contains a GetPinnableReference() method that returns a ref T or ref readonly T may be fixed. 添加此功能意味着 fixed 可与 System.Span<T> 和相关类型配合使用。Adding this feature means that fixed can be used with System.Span<T> and related types.

有关详细信息,请参阅语言参考中的 fixed 语句一文。For more information, see the fixed statement article in the language reference.

增强的泛型约束Enhanced generic constraints

现在,可以将类型 System.EnumSystem.Delegate 指定为类型参数的基类约束。You can now specify the type System.Enum or System.Delegate as base class constraints for a type parameter.

现在也可以使用新的 unmanaged 约束来指定类型参数必须是不可为 null 的“非托管类型”You can also use the new unmanaged constraint, to specify that a type parameter must be a non-nullable unmanaged type.

有关详细信息,请参阅有关 where 泛型约束类型参数的约束的文章。For more information, see the articles on where generic constraints and constraints on type parameters.

将这些约束添加到现有类型是不兼容的更改Adding these constraints to existing types is an incompatible change. 封闭式泛型类型可能不再满足这些新约束的要求。Closed generic types may no longer meet these new constraints.

提升了现有功能Make existing features better

第二个主题提供了对语言中的功能的改进。The second theme provides improvements to features in the language. 这些功能提升了在编写 C# 时的效率。These features improve productivity when writing C#.

元组支持 ==!=Tuples support == and !=

C# 元组类型现在支持 ==!=The C# tuple types now support == and !=. 有关详细信息,请参阅有关元组一文中的转换等式部分。For more information, see the section covering equality in the article on tuples.

将特性添加到自动实现的属性的支持字段Attach attributes to the backing fields for auto-implemented properties

现在支持此语法:This syntax is now supported:

[field: SomeThingAboutFieldAttribute]
public int SomeProperty { get; set; }

属性 SomeThingAboutFieldAttribute 应用于编译器生成的 SomeProperty 的支持字段。The attribute SomeThingAboutFieldAttribute is applied to the compiler generated backing field for SomeProperty. 有关详细信息,请参阅 C# 编程指南中的属性For more information, see attributes in the C# programming guide.

in 方法重载解析决胜属性in method overload resolution tiebreaker

在添加 in 参数修饰符时,这两个方法将导致多义性:When the in argument modifier was added, these two methods would cause an ambiguity:

static void M(S arg);
static void M(in S arg);

现在,通过值(前面示例中的第一个)的重载比通过只读引用版本的重载更好。Now, the by value (first in the preceding example) overload is better than the by readonly reference version. 若要使用只读引用参数调用版本,必须在调用方法前添加 in 修饰符。To call the version with the readonly reference argument, you must include the in modifier when calling the method.

备注

这将作为 bug 修复来实现。This was implemented as a bug fix. 即使在设置为“7.2”的语言版本中,这也不再具有多义性。This no longer is ambiguous even with the language version set to "7.2".

有关详细信息,请参阅有关 in 参数修饰符的文章。For more information, see the article on the in parameter modifier.

扩展初始值设定项中的表达式变量Extend expression variables in initializers

已对在 C# 7.0 中添加的允许 out 变量声明的语法进行了扩展,以包含字段初始值设定项、属性初始值设定项、构造函数初始值设定项和查询子句。The syntax added in C# 7.0 to allow out variable declarations has been extended to include field initializers, property initializers, constructor initializers, and query clauses. 它允许使用如以下示例中所示的代码:It enables code such as the following example:

public class B
{
   public B(int i, out int j)
   {
      j = i;
   }
}

public class D : B
{
   public D(int i) : base(i, out var j)
   {
      Console.WriteLine($"The value of 'j' is {j}");
   }
}

改进了重载候选项Improved overload candidates

在每个版本中,对重载解析规则进行了更新,以解决多义方法调用具有“明显”选择的情况。In every release, the overload resolution rules get updated to address situations where ambiguous method invocations have an "obvious" choice. 此版本添加了三个新规则,以帮助编译器选取明显的选择:This release adds three new rules to help the compiler pick the obvious choice:

  1. 当方法组同时包含实例和静态成员时,如果方法在不含实例接收器或上下文的情况下被调用,则编译器将丢弃实例成员。When a method group contains both instance and static members, the compiler discards the instance members if the method was invoked without an instance receiver or context. 如果方法在含有实例接收器的情况下被调用,则编译器将丢弃静态成员。The compiler discards the static members if the method was invoked with an instance receiver. 在没有接收器时,编译器将仅添加静态上下文中的静态成员,否则,将同时添加静态成员和实例成员。When there is no receiver, the compiler includes only static members in a static context, otherwise both static and instance members. 当接收器是不明确的实例或类型时,编译器将同时添加两者。When the receiver is ambiguously an instance or type, the compiler includes both. 静态上下文(其中隐式 this 实例接收器无法使用)包含未定义 this 的成员的正文(例如,静态成员),以及不能使用 this 的位置(例如,字段初始值设定项和构造函数初始值设定项)。A static context, where an implicit this instance receiver cannot be used, includes the body of members where no this is defined, such as static members, as well as places where this cannot be used, such as field initializers and constructor-initializers.
  2. 当一个方法组包含类型参数不满足其约束的某些泛型方法时,这些成员将从候选集中移除。When a method group contains some generic methods whose type arguments do not satisfy their constraints, these members are removed from the candidate set.
  3. 对于方法组转换,返回类型与委托的返回类型不匹配的候选方法将从集中移除。For a method group conversion, candidate methods whose return type doesn't match up with the delegate's return type are removed from the set.

你将注意到此更改,因为当你确定哪个方法更好时,你将发现多义方法重载具有更少的编译器错误。You'll only notice this change because you'll find fewer compiler errors for ambiguous method overloads when you are sure which method is better.

新的编译器选项New compiler options

新的编译器选项支持 C# 程序的新版本和 DevOps 方案。New compiler options support new build and DevOps scenarios for C# programs.

公共或开放源代码签名Public or Open Source signing

-publicsign 编译器选项指示编译器使用公钥对程序集进行签名。The -publicsign compiler option instructs the compiler to sign the assembly using a public key. 程序集被标记为已签名,但签名取自公钥。The assembly is marked as signed, but the signature is taken from the public key. 此选项使你能够使用公钥在开放源代码项目中构建签名的程序集。This option enables you to build signed assemblies from open-source projects using a public key.

有关详细信息,请参阅 -publicsign 编译器选项一文。For more information, see the -publicsign compiler option article.

pathmappathmap

-pathmap 编译器选项指示编译器将生成环境中的源路径替换为映射的源路径。The -pathmap compiler option instructs the compiler to replace source paths from the build environment with mapped source paths. -pathmap 选项控制由编译器编写入 PDB 文件或为 CallerFilePathAttribute 编写的源路径。The -pathmap option controls the source path written by the compiler to PDB files or for the CallerFilePathAttribute.

有关详细信息,请参阅 -pathmap 编译器选项一文。For more information, see the -pathmap compiler option article.