csharplang/proposals/csharp-7.1/infer-tuple-names.md
Bill Wagner 74f60810a3 markdown lint for C# 7.1 proposals
These had two sets of changes:

1. use `csharp` as the language identifier for code fences.
1. use relative links to other articles in the dotnet/csharplang repo that are being published on docs.microsoft.com so those links resolve to the article published there from that site.
2019-03-07 11:50:08 -05:00

3.2 KiB

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. 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. For instance, new { x.f1, y?.f2 } declares members "f1" and "f2".

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.
      • In C#, this allows three cases: y (identifier), x.y (simple member access) and x?.y (conditional access).
      • In VB, this allows for additional cases, such as x.y().
    • Rejecting reserved tuple names (case-sensitive in C#, case-insensitive in VB), as they are either forbidden or already implicit. For instance, such as ItemN, Rest, and ToString.
    • 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. 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.

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));

The same would also apply to VB tuples, using the VB-specific rules for inferring name from expression and case-insensitive name comparisons.

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

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.

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