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 rewritten sample we can see that several lines of code can be saved when obtaining from an object several values within 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 deconstruction site the compiler emits an error about ambiguous call:
- A
Deconstruct()
method can be an extension method: this is especially useful to deconstruct a .NET 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 provides deconstruction like for example
System.Collections.Generic.KeyValuePair<TKey,TValue>
. The only other class that has a isSystem.Collections.DictionaryEntry
and 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 one could expect, deconstruction works especially well with tuples. This is shown by this 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 use 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<>
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 in order 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.