.NET10之C# Target-typed new expression深入解析

一、基本概念与语法

目标类型 new() (Target-typed new expression)是 C# 9.0 引入的核心语言特性,允许在目标类型已知的上下文 中省略 new 关键字后的类型名称,编译器会根据上下文自动推断要创建的对象类型。

核心语法对比

传统写法 目标类型 new() 写法 说明
List<int> list = new List<int>(); List<int> list = new(); 无参构造函数
List<int> list = new List<int>(100); List<int> list = new(100); 带参数构造函数
List<int> list = new List<int> { 1, 2, 3 }; List<int> list = new() { 1, 2, 3 }; 对象/集合初始化器
Dictionary<string, List<int>> dict = new Dictionary<string, List<int>>(); Dictionary<string, List<int>> dict = new(); 复杂泛型类型

关键规则

  1. 必须有明确目标类型 :不能在 var 声明中使用 new(),因为目标类型未知

    csharp 复制代码
    // 错误:无法推断类型
    // var list = new(); 
    
    // 正确:目标类型明确
    List<int> list = new();
  2. 适用场景:适用于所有有公共构造函数的引用类型和值类型(枚举除外)

  3. 完整语法形式

    antlr 复制代码
    target_typed_new: 'new' '(' argument_list? ')' object_or_collection_initializer?;

二、解决的核心问题

1. 消除类型重复,提高代码简洁性

在复杂类型初始化时,无需重复冗长的类型名称,尤其适用于嵌套泛型类型

csharp 复制代码
// 传统写法
Dictionary<string, Dictionary<int, List<string>>> data = 
    new Dictionary<string, Dictionary<int, List<string>>>();

// 目标类型 new() 写法
Dictionary<string, Dictionary<int, List<string>>> data = new();

2. 简化字段与属性初始化

无需复制类型名称,使代码更易维护,减少拼写错误风险:

csharp 复制代码
// 类中的字段初始化
private readonly Dictionary<string, List<LogEntry>> _logs = new()
{
    ["Error"] = new(),
    ["Warning"] = new(),
    ["Info"] = new()
};

3. 优化方法参数传递

当方法参数类型明确时,可直接使用 new() 创建实例:

csharp 复制代码
// 方法定义
public void ProcessData(Options options) { ... }

// 调用方法(传统写法)
ProcessData(new Options { Timeout = 30, RetryCount = 3 });

// 调用方法(目标类型 new() 写法)
ProcessData(new { Timeout = 30, RetryCount = 3 });

4. 支持 throw 语句中的异常创建

csharp 复制代码
// 目标类型为 System.Exception
throw new();

// 带参数的异常
throw new("Invalid operation", innerException);

三、生产环境使用场景

1. 集合与复杂对象初始化(最常用)

在业务逻辑中创建各种集合类型,尤其是在配置初始化、数据缓存等场景:

csharp 复制代码
// 配置初始化
var appSettings = new AppSettings
{
    ConnectionStrings = new()
    {
        ["Default"] = "Server=...",
        ["Redis"] = "localhost:6379"
    },
    Logging = new()
    {
        LogLevel = new()
        {
            ["Default"] = LogLevel.Information,
            ["Microsoft"] = LogLevel.Warning
        }
    }
};

2. 依赖注入配置

ASP.NET Core 等框架中配置服务时,简化选项初始化:

csharp 复制代码
// Startup.cs 或 Program.cs
builder.Services.Configure<MvcOptions>(options =>
{
    options.Filters.Add(new() // 推断为 ServiceFilterAttribute
    {
        ServiceType = typeof(ValidationFilter)
    });
});

3. 数据传输对象(DTO)创建

在 API 开发中创建请求/响应对象,减少重复代码:

csharp 复制代码
// 创建订单请求
var createOrderRequest = new CreateOrderRequest
{
    CustomerId = "CUST-123",
    Items = new()
    {
        new() { ProductId = "PROD-456", Quantity = 2, UnitPrice = 19.99m },
        new() { ProductId = "PROD-789", Quantity = 1, UnitPrice = 49.99m }
    },
    ShippingAddress = new()
    {
        Street = "123 Main St",
        City = "Seattle",
        State = "WA",
        PostalCode = "98101"
    }
};

4. 单元测试中的对象构建

在测试代码中快速创建测试数据,提高可读性:

csharp 复制代码
[Test]
public void CalculateTotal_WithDiscount_ReturnsCorrectValue()
{
    // Arrange
    var order = new Order
    {
        Id = 1,
        CustomerId = "CUST-123",
        Items = new()
        {
            new() { ProductName = "Laptop", Price = 999.99m, Quantity = 1 },
            new() { ProductName = "Mouse", Price = 29.99m, Quantity = 2 }
        },
        Discount = new() // 推断为 PercentageDiscount
        {
            Rate = 0.1m // 10% 折扣
        }
    };
    
    // Act
    var total = order.CalculateTotal();
    
    // Assert
    Assert.AreEqual(999.99m + 29.99m*2-(999.99m + 29.99m*2)*0.1m, total);
}

5. 事件与委托处理

在注册事件处理程序时,简化委托实例创建:

csharp 复制代码
// 声明事件
public event EventHandler<DataReceivedEventArgs> DataReceived;

// 触发事件(传统写法)
DataReceived?.Invoke(this, new DataReceivedEventArgs(data));

// 触发事件(目标类型 new() 写法)
DataReceived?.Invoke(this, new(data));

6. 不可变类型与记录(Record)初始化

C# 9.0+ 中的记录类型结合目标类型 new(),使不可变对象创建更简洁:

csharp 复制代码
public record Person(string FirstName, string LastName, int Age);

// 创建记录实例
Person person = new("John", "Doe", 30);

// 使用 with 表达式创建新实例
Person updatedPerson = person with { Age = 31 };

四、完整可运行代码示例

以下是一个包含多种场景的完整示例,可直接在 C# 9.0+ 环境中运行:

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Linq;

// 1. 简单类型与集合初始化
void BasicUsage()
{
    Console.WriteLine("=== 基本用法 ===");
    
    // 列表初始化
    List<string> fruits = new() { "Apple", "Banana", "Cherry" };
    Console.WriteLine($"Fruits: {string.Join(", ", fruits)}");
    
    // 字典初始化
    Dictionary<int, string> numberNames = new()
    {
        [1] = "One",
        [2] = "Two",
        [3] = "Three"
    };
    Console.WriteLine($"Number 2: {numberNames[2]}");
    
    // 带参数构造函数
    List<int> numbers = new(10); // 初始容量为 10
    numbers.AddRange(Enumerable.Range(1, 5));
    Console.WriteLine($"Numbers count: {numbers.Count}, Capacity: {numbers.Capacity}");
}

// 2. 嵌套复杂对象
void NestedObjects()
{
    Console.WriteLine("\n=== 嵌套对象 ===");
    
    var company = new Company
    {
        Name = "Tech Corp",
        Departments = new()
        {
            new()
            {
                Name = "Engineering",
                Employees = new()
                {
                    new() { Id = 1, Name = "Alice", Position = "Developer" },
                    new() { Id = 2, Name = "Bob", Position = "Designer" }
                }
            },
            new()
            {
                Name = "Sales",
                Employees = new()
                {
                    new() { Id = 3, Name = "Charlie", Position = "Sales Manager" }
                }
            }
        }
    };
    
    Console.WriteLine($"Company: {company.Name}");
    foreach (var dept in company.Departments)
    {
        Console.WriteLine($"  Department: {dept.Name}");
        foreach (var emp in dept.Employees)
        {
            Console.WriteLine($"    - {emp.Name} ({emp.Position})");
        }
    }
}

// 3. 方法参数与异常处理
void MethodParametersAndExceptions()
{
    Console.WriteLine("\n=== 方法参数与异常 ===");
    
    // 方法参数传递
    ProcessOrder(new()
    {
        OrderId = "ORD-789",
        Items = new()
        {
            new() { ProductId = "PROD-100", Quantity = 2 }
        }
    });
    
    // 异常抛出
    try
    {
        ValidateOrder(null);
    }
    catch (ArgumentNullException ex)
    {
        Console.WriteLine($"Caught expected exception: {ex.Message}");
    }
}

// 辅助类定义
public class Company
{
    public string Name { get; set; } = string.Empty;
    public List<Department> Departments { get; set; } = new();
}

public class Department
{
    public string Name { get; set; } = string.Empty;
    public List<Employee> Employees { get; set; } = new();
}

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string Position { get; set; } = string.Empty;
}

public class Order
{
    public string OrderId { get; set; } = string.Empty;
    public List<OrderItem> Items { get; set; } = new();
}

public class OrderItem
{
    public string ProductId { get; set; } = string.Empty;
    public int Quantity { get; set; }
}

// 辅助方法
public static void ProcessOrder(Order order)
{
    Console.WriteLine($"Processing order: {order.OrderId}");
    foreach (var item in order.Items)
    {
        Console.WriteLine($"  Product {item.ProductId}: {item.Quantity} units");
    }
}

public static void ValidateOrder(Order? order)
{
    if (order == null)
        throw new ArgumentNullException(nameof(order), "Order cannot be null");
}

// 主程序
public class Program
{
    public static void Main()
    {
        BasicUsage();
        NestedObjects();
        MethodParametersAndExceptions();
    }
}

代码运行结果

复制代码
=== 基本用法 ===
Fruits: Apple, Banana, Cherry
Number 2: Two
Numbers count: 5, Capacity: 10

=== 嵌套对象 ===
Company: Tech Corp
  Department: Engineering
    - Alice (Developer)
    - Bob (Designer)
  Department: Sales
    - Charlie (Sales Manager)

=== 方法参数与异常 ===
Processing order: ORD-789
  Product PROD-100: 2 units
Caught expected exception: Order cannot be null (Parameter 'order')

四、最佳实践与注意事项

1. 适用与不适用场景对比

推荐使用场景 不推荐/不适用场景
字段/属性初始化,尤其是复杂类型 使用 var 声明变量时
方法参数传递(类型明确) 枚举类型(无构造函数)
异常抛出(throw new() 接口类型(无法直接实例化)
集合初始化(List、Dictionary 等) 数组创建(需要特殊语法)
嵌套对象创建 动态类型(dynamic

2. 与 var 的区别

特性 目标类型 new() var
类型推断方式 从目标类型推断 从右侧表达式推断
适用场景 目标类型明确时 右侧类型明确时
语法形式 Type variable = new(); var variable = new Type();
可读性 类型在左侧,一目了然 类型在右侧,需查看

3. 版本兼容性

  • 目标类型 new() 是 C# 9.0 及以上版本 的特性

  • 若需在旧版本项目中使用,需升级项目的语言版本:

    xml 复制代码
    <!-- .csproj 文件 -->
    <PropertyGroup>
      <LangVersion>9.0</LangVersion>
    </PropertyGroup>

五、总结

目标类型 new() 是 C# 语言的重要改进,它通过上下文类型推断消除了重复的类型名称,使代码更加简洁、易读和易维护。在生产环境中,它特别适合集合初始化、复杂对象创建、方法参数传递等场景,能显著提高开发效率并减少错误。

核心价值:在保持类型安全的前提下,平衡了代码简洁性与可读性,是现代 C# 开发中不可或缺的语法糖。

相关推荐
别抢我的锅包肉2 小时前
【python-Pyspark】环境搭建及案例(Windows)
windows
long_songs2 小时前
Python编程第02课:Windows/Mac/Linux环境安装配置详解
windows·python·macos
百事牛科技2 小时前
高效办公技巧:如何取消PPT以“只读方式”打开?
windows·powerpoint
这辈子谁会真的心疼你2 小时前
怎么修改pdf文档属性?介绍三个方法
数据库·pdf·c#
BIBI20492 小时前
VirtualBox 7.x 安装 Ubuntu 24 及增强功能配置、克隆虚拟机教程
linux·windows·ubuntu·环境搭建·安装教程·最佳实践·virtualbox
Master_H_ice12 小时前
Claude Code安装试用记录(Windows)
windows·claude code
芳草萋萋鹦鹉洲哦15 小时前
【windows】nginx如何注册为开机自启的服务(WinSW实现)
运维·windows·nginx
好名字更能让你们记住我15 小时前
vmware虚拟机安装Windows10镜像【超详细图文版】!!!
windows·系统安装·vmware·虚拟机·图文教程
副露のmagic16 小时前
字符串章节 leetcode 思路&实现
windows·python·leetcode