C# 7.0 introduced the deconstruction syntax. It allows developers to extract in a single expression, properties of an object or elements of a tuple and then to assign them to distinct variables. Here is a small program candidate to be simplified with deconstruction:
1 2 3 4 5 6 7 8 9 |
var pat = new Person() { Name = "Patrick", BirthDate = new DateOnly(1975, 4, 24) }; var name = pat.Name; var birthDate = pat.BirthDate; Console.WriteLine($"Name:{name} BirthDate:{birthDate:yyy-M-dd}"); class Person { internal string Name { get; init; } internal DateOnly BirthDate { get; init; } } |
Here is the same example with deconstructing the object pat
in the line var (name, birthDate) = pat;
thanks to a new method name Deconstruct()
:
1 2 3 4 5 6 7 8 9 10 11 12 |
var pat = new Person() { Name = "Patrick", BirthDate = new DateOnly(1975, 4, 24) }; var (name, birthDate) = pat; Console.WriteLine($"Name:{name} BirthDate:{birthDate:yyy-M-dd}"); class Person { internal string Name { get; init; } internal DateOnly BirthDate { get; init; } internal void Deconstruct(out string name, out DateOnly birthDate) { name = Name; birthDate = BirthDate; } } |
In this revised example, we see that numerous lines of code can be conserved by extracting multiple values from an object into several local variables. Thanks to deconstruction this can be achieved in a single expression.
If we decompile this example we can see that the C# compiler calls: pat.Deconstruct(out var name, out var birthDate);
Here are a few remarks about deconstruction capabilities:
- The C# compiler expects the method to be named
Deconstruct
. This is duck typing, this doesn’t work with another method name or if the methodDeconstruct()
returns something else thanvoid
. - If you don’t need to deconstruct one or several properties, just use the discard character underscore
_
as in :var (name, _) = pat;
record
andrecord struct
types automatically provides aDeconstruct()
method:
1 2 3 4 |
var pat = new Person("Patrick", new DateOnly(1975, 4, 24)); var (name, birthDate) = pat; record struct Person(string Name, DateOnly BirthDate); |
- Several
Deconstruct()
methods can be provided as long as the number ofout
parameters is different. If twoDeconstruct()
methods with the same number ofout
parameters are provided, at the deconstruction site the compiler emits an error about an ambiguous call:
- A method
Deconstruct()
can be an extension method: this is especially useful to deconstruct a .NET Base Class Library (BCL) class your custom way, like for example to deconstruct aDateOnly
:
1 2 3 4 5 6 7 8 9 10 |
var date = new DateOnly(1975, 4, 24); var (year, month, day) = date; static class ExtensionMethods { internal static void Deconstruct(this DateOnly date, out int year, out int month, out int day) { year = date.Year; month = date.Month; day = date.Day; } } |
- A few types in the .NET BCL provide deconstruction like for example
System.Collections.Generic.KeyValuePair<TKey,TValue>
. The only other class that offers deconstruction isSystem.Collections.DictionaryEntry
. There are also someDeconstruct()
extension methods forTuple<>
as explained in the next section.
1 2 3 4 5 |
var dictionary = new Dictionary<string, DateOnly>() { { "Patrick", new DateOnly(1975, 4, 24) } }; var (name, birthDate) = dictionary.Single(); Console.WriteLine($"Name:{name} BirthDate:{birthDate:yyy-M-dd}"); |
Deconstructing Tuples
As anticipated, deconstruction is particularly effective with tuples. This is demonstrated by the following sample program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// no deconstruction, just gather a value tuple from GetAddress() var valueTyple = GetAddress(); // deconstructing a tuple into variables typed (string city1, string zip1, string country1) = GetAddress(); // deconstructing a tuple into variables declared with var var (city2, zip2, country2) = GetAddress(); // deconstructing a tuple into variables that have already been declared. string city3, zip3, country3; (city3, zip3, country3) = GetAddress(); // Since C#10 one can use a mix of variable declaration and variables already declared string country4; (string city4, string zip4, country4) = GetAddress(); static (string city, string zip, string country) GetAddress() { return ("Tamarin", "90213", "Mauritius"); } |
Notice that under the hood, the compiler uses the type System.ValueTuple<T1,T2,T3>
and keeps track of named parameters city zip country
mapping with the corresponding value tuple fields valueTuple.Item1
, valueTuple.Item2
and valueTuple.Item3
.
ValueTuple<>
structures were introduced with C# 7.0 to improve Tuple<>
classes in terms of performance and syntax. Deconstruction also works with Tuple<>
classes as shown by the program below:
1 2 3 4 5 6 7 8 9 10 11 12 |
(string city1, string zip1, string country1) = GetAddress(); var (city2, zip2, country2) = GetAddress(); string city3, zip3, country3; (city3, zip3, country3) = GetAddress(); string country4; (string city4, string zip4, country4) = GetAddress(); static Tuple<string,string,string> GetAddress() { return new Tuple<string, string, string>("Tamarin", "90213", "Mauritius"); } |
Conclusion
With deconstruction, C# proposes a convenient way to reduce boilerplate code to write more concise code.
Why?
As written “It allows developers to extract in a single expression, properties of an object or elements of a tuple and then to assign them to distinct variables ”
In other words one can save lines of code when obtaining several values from an object in several local variables. Thanks to deconstruction this can be done in a single expression.