.NET 6中两种C#对象创建写法的差异及优劣势对比
核心结论
两种写法不存在运行时层面的本质区别,编译后生成的IL代码完全一致,没有任何性能差异。二者的区别仅存在于C#语法层面,属于不同版本推出的语法糖,适用场景有少量差异。
性能表现
两种写法的编译结果完全相同:最终都是在托管堆分配List<string>实例,在栈上持有对应类型的引用,不会产生任何额外的执行开销或者内存占用,运行时表现100%一致,不存在谁快谁慢的问题。
语法规则差异
两种写法依托不同的C#语法特性实现,规则边界有明显区别:
- 写法1
var list = new List<string>();用的是C# 3.0推出的隐式局部变量特性:编译器通过右侧初始化表达式的类型,自动推断局部变量list的静态类型为List<string>。注意var只能用于声明方法内的局部变量,不能用来定义类的字段、属性,也不能用于方法签名的参数、返回值位置。 - 写法2
List<string> list = new();用的是C# 9.0推出的目标类型new表达式特性:编译器通过左侧显式声明的变量类型(或其他赋值目标的类型,比如方法参数、返回值、成员初始化位),自动推断new关键字要实例化的目标类型为List<string>。这个特性没有使用位置限制,只要赋值目标有明确、可实例化的具体类型(不能是接口、抽象类)就可以正常使用。
额外提一个编译层面的约束:你不能写var list = new();,这种写法左右两侧都没有给编译器提供明确的类型信息,会直接编译报错。
适用场景与优劣势对比
两种写法在局部变量声明、且变量类型和实例化类型完全一致的场景下完全等价,选哪个只需要遵循团队的编码规范即可,没有绝对的优劣。二者各自有独有的适用场景:
- 写法1(var+完整new表达式)的独有场景:
- 配合匿名类型使用,比如LINQ查询生成的匿名类型结果,因为匿名类型没有可以手动书写的类型名,只能用var接收,比如
var queryResult = list.Select(x => new { x.Id, x.Name }).ToList(); - 当你不需要显式修改变量类型,希望直接以实例化的具体类型作为变量类型,且类型名不长的时候,写法更符合多数C#开发者的长期书写习惯。
- 配合匿名类型使用,比如LINQ查询生成的匿名类型结果,因为匿名类型没有可以手动书写的类型名,只能用var接收,比如
- 写法2(显式类型声明+目标类型new)的独有优势:
- 当变量类型是长泛型、嵌套泛型的时候,可以避免左右两侧重复书写一长串相同的类型名,大幅减少冗余代码,比如
Dictionary<string, List<(int UserId, string UserName)>> cache = new();比左右重复写一遍长泛型要简洁很多。 - 可以用于类的字段、属性初始化场景,这是var做不到的,比如你可以在类里写
public List<string> Tags { get; init; } = new();,但不能用var声明这个属性。 - 如果你需要把变量声明为实例类型的基类,只要new的目标类型和变量类型一致就可以正常使用,不需要重复写类型名。
- 当变量类型是长泛型、嵌套泛型的时候,可以避免左右两侧重复书写一长串相同的类型名,大幅减少冗余代码,比如
注意:如果你要把变量声明为接口/抽象父类类型,而实例化的是对应的实现类/派生类,两种写法都不能省略类型名:比如你要声明
IList<string> list = new List<string>();,这时候没法用new()简化,因为编译器会尝试直接实例化接口IList<string>,会直接编译报错。
内容的提问来源于stack exchange,提问作者user19187727
相关产品推荐
相关产品推荐

