.NET 10 新增功能概览

类库方面的改进

用字符串比较数字排序

在 .NET 10 中,System.String 类新增了 CompareAsNumbers 方法,用于按数字顺序比较字符串。这对于包含数字的字符串排序非常有用,例如文件名或版本号。

StringComparer numericStringComparer = StringComparer.Create(CultureInfo.CurrentCulture, CompareOptions.NumericOrdering);

Console.WriteLine(numericStringComparer.Equals("02", "2"));
// Output: True

foreach (string os in new[] { "Windows 8", "Windows 10", "Windows 11" }.Order(numericStringComparer))
{
    Console.WriteLine(os);
}

// Output:
// Windows 8
// Windows 10
// Windows 11

HashSet<string> set = new HashSet<string>(numericStringComparer) { "007" };
Console.WriteLine(set.Contains("7"));
// Output: True

对十六进制字符串转换的 UTF-8 支持

.NET 10 在 Convert 类中增加了对十六进制字符串转换操作的 UTF-8 支持。 这些新方法提供了在 UTF-8 字节序列和十六进制表示形式之间转换的有效方法,而无需中间字符串分配:

Convert.FromHexString(ReadOnlySpan<Byte>)
Convert.FromHexString(ReadOnlySpan<Byte>, Span<Byte>, Int32, Int32)
Convert.TryToHexString(ReadOnlySpan<Byte>, Span<Byte>, Int32)
Convert.TryToHexStringLower(ReadOnlySpan<Byte>, Span<Byte>, Int32)
Convert.TryToHexStringUpper(ReadOnlySpan<Byte>, Span<Byte>, Int32)

禁止重复 JSON 属性的选项

JSON 规范不指定在反序列化 JSON 有效负载时如何处理重复属性。 这可能会导致意外的结果和安全漏洞。NET 10 引入了 JsonSerializerOptions.AllowDuplicateProperties 禁止重复 JSON 属性的选项。

var options = new JsonSerializerOptions
{
    AllowDuplicateProperties = false
};

string json = @"{ ""Name"": ""Alice"", ""Name"": ""Bob"" }";

try
{
    var person = JsonSerializer.Deserialize<Person>(json, options);
}
catch (JsonException ex)
{
    Console.WriteLine($"JSON 反序列化失败: {ex.Message}");
}

SDK 中的改进

单次工具执行

现在 dotnet tool exec 可以使用命令执行 .NET 工具,而无需全局或本地安装该工具。

dotnet tool exec dotnetsay  "Hello, World!"

新的 dnx 工具执行脚本

该 dnx 脚本提供了一种简化的方式来执行工具。

dnx dotnetsay "Hello, World!"

基于文件的应用程序

Console.WriteLine("Hello, World!");

可以通过将代码保存到名为 app.cs 的文件中并运行以下命令来执行:

dotnet run app.cs
#:package Colorful.Console@1.2.15
Colorful.Console.WriteAscii("Hello, World!");

可以通过将代码保存到名为 app.cs 的文件中并运行以下命令来执行:

dotnet run app.cs

参阅 C# 预处理器指令

更一致的命令顺序

.NET CLI 命令现在遵循更一致的顺序模式。下表显示了新的优先形式和别名:

新名词优先形式 别名
dotnet package add dotnet add package
dotnet package list dotnet list package
dotnet package remove dotnet remove package
dotnet reference add dotnet add reference
dotnet reference list dotnet list reference
dotnet reference remove dotnet remove reference

C# 14 中的新功能

扩展成员

扩展成员允许您为现有类型添加新方法、属性或事件,而无需修改原始类型的定义。 这对于向第三方库添加功能特别有用。

很久就支持的扩展方法

public static class StringExtensions
{
    public static bool ContainsNumber(this string str)
    {
        return str.Any(char.IsDigit);
    }
}

新增的扩展成员语法

public static class StringExtensions
{
    // Extension block for string
    extension(string source)
    {
        // Extension property:
        public bool ContainsNumber => source.Any(char.IsDigit);

        // Extension method:
        public string Repeat(int count)
        {
            return string.Concat(Enumerable.Repeat(source, count));
        }
    }

    // Static extension block for string
    extension(string)
    {
        // Static extension method:
        public static string Combine(string first, string second, string? separator = null)
        {
            return $"{first}{separator}{second}";
        }

        // Static extension property:
        public static string SpaceChar => " ";

        // Static user defined operator:
        public static string operator * (string str, int count)
        {
            return string.Concat(Enumerable.Repeat(str, count));
        }
    }
}

使用示例:

string text = "Hello";

Console.WriteLine(text.ContainsNumber); // False
Console.WriteLine(text.Repeat(3)); // HelloHelloHello

string combined = string.Combine("Hello", "World", ", ");
Console.WriteLine(combined); // Hello, World

Console.WriteLine(string.SpaceChar); // " "

string repeated = "Hi" * 3;
Console.WriteLine(repeated); // HiHiHi

新增 field 关键字

private string _msg;
public string Message
{
    get => _msg;
    set => _msg = value ?? throw new ArgumentNullException(nameof(value));
}

可以简化为:

public string Message
{
    get => field;
    set => field = value ?? throw new ArgumentNullException(nameof(value));
}

未绑定泛型类型和 nameof

在 C# 14 中,nameof 运算符现在支持未绑定的泛型类型。这允许您获取泛型类型的名称,而无需指定类型参数。

Console.WriteLine(nameof(List<>)); // Output: List
Console.WriteLine(nameof(List<int>)); // Output: List
Console.WriteLine(nameof(Dictionary<,>)); // Output: Dictionary

自定义复合赋值

C# 14 允许您为自定义类型定义复合赋值运算符。这使得代码更简洁,并提高了可读性。

public class Point(int x, int y)
{
    public int X { get; set; } = x;

    public int Y { get; set; } = y;

    public static Point operator +(Point a, Point b)
    {
        return new Point(a.X + b.X, a.Y + b.Y);
    }

    public static Point operator ++(Point p)
    {
        return new Point(p.X + 1, p.Y + 1);
    }
}

使用示例:

Point p1 = new Point { X = 1, Y = 2 };
Point p2 = new Point { X = 3, Y = 4 };

Point p3 = p1 + p2; // Output: p3.X = 4, p3.Y = 6
p1++; // Now p1.X = 5, p1.Y = 7

Null 条件分配

在 C# 14 之前,在分配给属性之前,需要对变量进行 null 检查:

if (obj != null)  // or obj is not null
{
    obj.Property = newValue();
}

可以简化为:

obj?.Property = newValue();