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;
An appropriate deconstruct overload is located for
Product
, in this case, because you're usingvar
type inference, the deconstructor must have 2 parameters (but any type).The out variables are then assigned to your deconstruct variables, which you've also named
string name
andint 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"
};