What is the variable declaration "var (name, categoryId) = object" called in C#?

Note that the syntax

var (name, categoryId) = product;

is a deconstruction - it is NOT an assignment to a tuple.

From the docs

Starting with C# 7.0, you can retrieve multiple elements from a tuple or retrieve multiple field, property, and computed values from an object in a single deconstruct operation. When you deconstruct a tuple, you assign its elements to individual variables. When you deconstruct an object, you assign selected values to individual variables.

Ignoring Deconstruct for a moment, any tuple can be deconstructed into individual variables, provided that sufficient variables (or the discard, _) be provided to accomodate the tuple.

e.g.

(string name, int categoryId) = ("Hello", 123);

Assigns "Hello" to name, and 123 to categoryId

All of the below are equivalent

(string name, int categoryId) = ("Hello", 123); // Types of tuple match position vars
(var name, var categoryId) = ("Hello", 123); // Type variable types are inferred
var (name, categoryId) = ("Hello", 123);

Similarly, by providing suitable Deconstruct overloads or extension methods for your own classes / records, you can assign multiple variables to the out parameters of the matched Deconstruct method:

var (name, categoryId) = Product; 

which tells the compiler to look for an appropriate Deconstruct overload for Product. In this case, because you're using var type inference for all the deconstructed, the deconstructor must have 2 parameters (of any type, which will be inferred).

There are some other nuances happening here.

Firstly, as you've seen, you can declare many different Deconstructions for your Product record, as long as the signatures of the deconstructions differ.

The (value) tuple syntax

public void Deconstruct(out string name, out int categoryId)
    => (name, categoryId) = (Name, CategoryId);

is just a convenient short hand for

public void Deconstruct(out string name, out int categoryId)
{
    name = Name;
    categoryId = CategoryId;
}

When you make the following assignment:

 var (name, categoryId) = product;
  1. An appropriate deconstruct overload is located for Product, in this case, because you're using var type inference, the deconstructor must have 2 parameters (but any type).

  2. The out variables are then assigned to your deconstruct variables, which you've also named string name and int categoryId.

Although you can't deconstruct directly INTO a System.ValueTuple or System.Tuple, you can deconstruct FROM both

var (name, categoryId) = Tuple.Create("Hello", 123); // Old Heap tuples

var (name, categoryId) = ("Hello", 123); // Newer value tuples

One of the major uses of Deconstruction is for short hand notation during Pattern matching, where you can quickly reason over the type and properties:

e.g. instead of

var result = product switch
{
  Product x when x.CategoryId == 3 => "You've got a category 3 product",
  Product x when string.IsNullOrWhiteSpace(x.Name) => "That product looks broken",
  _ => "Just a standard product"
};

You can instead deconstruct and / or discard as necessary:

var result2 = product switch
{
  var (_, cat) when cat == 3 => "You've got a category 3 product",
  var (str, _) when string.IsNullOrWhiteSpace(str) => "That product looks broken",
  _ => "Just a standard product"
};

Tags:

C#