推断元组名称(也称为Infer tuple names (aka. 元组投影初始值设定项)tuple projection initializers)

总结Summary

在许多常见情况下,此功能允许忽略元组元素名称,而不是推断。In a number of common cases, this feature allows the tuple element names to be omitted and instead be inferred. 例如,可以从 (x.f1, x?.f2)推断元素名称 "f1" 和 "f2",而不是键入 (f1: x.f1, f2: x?.f2)For instance, instead of typing (f1: x.f1, f2: x?.f2), the element names "f1" and "f2" can be inferred from (x.f1, x?.f2).

这与匿名类型的行为类似,允许在创建过程中推断成员名称。This parallels the behavior of anonymous types, which allow inferring member names during creation. 例如,new { x.f1, y?.f2 } 声明成员 "f1" 和 "f2"。For instance, new { x.f1, y?.f2 } declares members "f1" and "f2".

在 LINQ 中使用元组时,此操作特别有用:This is particularly handy when using tuples in LINQ:

// "c" and "result" have element names "f1" and "f2"
var result = list.Select(c => (c.f1, c.f2)).Where(t => t.f2 == 1); 

详细设计Detailed design

更改包括两个部分:There are two parts to the change:

  1. 尝试为没有显式名称的每个元组元素推断候选名称:Try to infer a candidate name for each tuple element which does not have an explicit name:
    • 使用与匿名类型的名称推理相同的规则。Using same rules as name inference for anonymous types.
      • 在C#中,这可以实现三种情况: y (标识符)、x.y (简单成员访问)和 x?.y (条件性访问)。In C#, this allows three cases: y (identifier), x.y (simple member access) and x?.y (conditional access).
      • 在 VB 中,这可以实现其他情况,如 x.y()In VB, this allows for additional cases, such as x.y().
    • 正在拒绝保留元组名称(在中C#区分大小写,不区分大小写),因为它们被禁止或已隐式。Rejecting reserved tuple names (case-sensitive in C#, case-insensitive in VB), as they are either forbidden or already implicit. 例如 ItemNRestToStringFor instance, such as ItemN, Rest, and ToString.
    • 如果任何候选名称在整个元组中都是C#重复的(在中区分大小写,不区分大小写),则删除这些候选项,If any candidate names are duplicates (case-sensitive in C#, case-insensitive in VB) within the entire tuple, we drop those candidates,
  2. 在转换过程中(这会对从元组文本中删除名称进行检查和发出警告),推断名称不会生成任何警告。During conversions (which check and warn about dropping names from tuple literals), inferred names would not produce any warnings. 这样可以避免破坏现有的元组代码。This avoids breaking existing tuple code.

请注意,处理重复项的规则与匿名类型的规则不同。Note that the rule for handling duplicates is different than that for anonymous types. 例如,new { x.f1, x.f1 } 生成错误,但仍允许 (x.f1, x.f1) (只是没有任何推断名称)。For instance, new { x.f1, x.f1 } produces an error, but (x.f1, x.f1) would still be allowed (just without any inferred names). 这样可以避免破坏现有的元组代码。This avoids breaking existing tuple code.

为保持一致性,将应用到析构(在中C#为)生成的元组:For consistency, the same would apply to tuples produced by deconstruction-assignments (in C#):

// tuple has element names "f1" and "f2" 
var tuple = ((x.f1, x?.f2) = (1, 2));

这同样也适用于 VB 元组,使用特定于 VB 的规则从表达式推断名称,不区分大小写的名称比较。The same would also apply to VB tuples, using the VB-specific rules for inferring name from expression and case-insensitive name comparisons.

将C# 7.1 编译器(或更高版本)与语言版本 "7.0" 一起使用时,将推断元素名称(尽管此功能不可用),但会出现用于尝试访问它们的使用站点错误。When using the C# 7.1 compiler (or later) with language version "7.0", the element names will be inferred (despite the feature not being available), but there will be a use-site error for trying to access them. 这将限制稍后会出现兼容性问题的新代码的添加(如下所述)。This will limit additions of new code that would later face the compatibility issue (described below).

缺点Drawbacks

主要缺点是,这会引入C# 7.0 的兼容性中断:The main drawback is that this introduces a compatibility break from C# 7.0:

Action y = () => M();
var t = (x: x, y);
t.y(); // this might have previously picked up an extension method called “y”, but would now call the lambda.

兼容性委员会发现,此中断是可接受的,因为它受到限制,并且自元组( C#在7.0 中)后的时间范围很短。The compatibility council found this break acceptable, given that it is limited and the time window since tuples shipped (in C# 7.0) is short.

参考References