目标类型的 new 表达式

注意

本文是特性规范。 该规范充当该功能的设计文档。 它包括建议的规范更改,以及功能设计和开发过程中所需的信息。 这些文章将发布,直到建议的规范更改最终确定并合并到当前的 ECMA 规范中。

功能规范与已完成的实现之间可能存在一些差异。 在相关的 语言设计会议(LDM)记录中捕捉到了这些差异。

可以在 规范一文中详细了解将功能规范采用 C# 语言标准的过程。

支持者问题:https://github.com/dotnet/csharplang/issues/100

总结

当类型已知时,不需要构造函数的类型规范。

动力

允许字段初始化,而无需复制类型。

Dictionary<string, List<int>> field = new() {
    { "item1", new() { 1, 2, 3 } }
};

允许在从使用情况推断类型时省略该类型。

XmlReader.Create(reader, new() { IgnoreWhitespace = true });

在不拼写类型的情况下实例化对象。

private readonly static object s_syncObj = new();

规范

接受一种新的语法形式,即 object_creation_expressiontarget_typed_new,其中类型是可选的。

object_creation_expression
    : 'new' type '(' argument_list? ')' object_or_collection_initializer?
    | 'new' type object_or_collection_initializer
    | target_typed_new
    ;
target_typed_new
    : 'new' '(' argument_list? ')' object_or_collection_initializer?
    ;

target_typed_new 表达式没有类型。 但是,有一个新的对象创建转换,它是表达式的隐式转换,该转换从 target_typed_new 起存在于每种类型中。

给定目标类型 T,如果 TSystem.Nullable的实例,那么类型 T0T的基础类型。 否则,T0T。 转换为 T 类型的 target_typed_new 表达式的含义,与指定 T0 类型的相应 object_creation_expression 的含义相同。

如果 target_typed_new 被用作一元运算符或二元运算符的操作数,或者在不涉及对象创建转换的情况下使用,则会导致编译时错误。

未结的问题:是否应允许委托和元组作为目标类型?

上述规则包括委托(引用类型)和元组(结构类型)。 尽管这两种类型都是可构造的,但如果类型是可推断的,则已经可以使用匿名函数或元组文本。

(int a, int b) t = new(1, 2); // "new" is redundant
Action a = new(() => {}); // "new" is redundant

(int a, int b) t = new(); // OK; same as (0, 0)
Action a = new(); // no constructor found

杂项

以下是规范的后果:

  • 允许 throw new()(目标类型为 System.Exception
  • 二进制运算符不允许使用目标类型的 new
  • 当没有目标类型时,不允许在以下位置中使用它:一元运算符、foreach 的集合,在 using 中、在析构函数中、在 await 表达式中、不允许用作匿名类型属性 (new { Prop = new() })、在 lock 语句中、在 sizeof 中、在 fixed 语句中、在成员访问 (new().field) 中、在动态调度的操作 (someDynamic.Method(new())) 中、在 LINQ 查询中、不允许用作 is 运算符的操作数、不允许用作 ?? 运算符的左操作数...
  • 也不允许将它作为 ref 标签。
  • 不允许将以下类型的类型作为转换的目标
    • 枚举类型:new() 将正常工作(因为 new Enum() 工作可提供默认值),但 new(1) 将不起作用,因为枚举类型没有构造函数。
    • 接口类型: 此操作与 COM 类型的相应创建表达式相同。
    • 数组类型: 数组需要特殊语法来提供长度。
    • 动态:我们不允许 new dynamic(),因此我们不允许以 dynamic 作为目标类型的 new()
    • 元组:其含义与使用基础类型来创建对象相同。
    • object_creation_expression 中不允许的所有其他类型也排除,例如指针类型。

缺点

有人担心目标类型的 new 会带来新类别的重大变化,但我们已经通过 nulldefault 解决了这个问题,所以这并不是什么大问题。

替代方案

关于类型太长而无法在字段初始化中重复的大多数抱怨都是关于类型参数而不是类型本身的,我们只能推断类型参数,如 new Dictionary(...) (或类似项),并从参数或集合初始化器中本地推断类型参数。

问题

  • 我们应该禁止表达式树中的用法吗? (否)
  • 该功能如何与 dynamic 参数交互? (无特殊待遇)
  • IntelliSense 应如何与 new()协同工作? (仅当存在单个目标类型时)

设计会议