An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
Deconstruction in C# is syntax that lets an expression be “broken apart” into separate variables in a single operation. It is used with tuples, records, and any user-defined type that provides a suitable Deconstruct method.
Using the Point example:
public readonly struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y) => (X, Y) = (x, y);
public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}
Because Point defines:
public void Deconstruct(out int x, out int y)
it can be used like this:
var p = new Point(3, 4);
var (x, y) = p; // deconstruction
// x == 3, y == 4
The compiler translates var (x, y) = p; roughly into:
int x, y;
p.Deconstruct(out x, out y);
So deconstruction is just syntactic sugar over calling a Deconstruct method (or using tuple elements) and assigning the results to variables.
Key concepts and where deconstruction is used:
- Tuples
- For a tuple:
(int a, int b) t = (1, 2); var (x, y) = t; // x = 1, y = 2 - The language knows how to deconstruct tuple types directly.
- For a tuple:
- User-defined types (
class,struct,record)- Any type can support deconstruction by defining one or more
Deconstructmethods:public void Deconstruct(out string fname, out string lname) - Then:
var (first, last) = person; - Multiple overloads are allowed as long as they differ in number of parameters (arity). Overloads with the same number of parameters are ambiguous and not allowed.
- Any type can support deconstruction by defining one or more
- Extension
Deconstructmethods- If a type cannot be modified, deconstruction can be added via extension methods:
public static void Deconstruct(this SomeType value, out int a, out int b) { ... } - Then instances of
SomeTypecan be deconstructed withvar (a, b) = someInstance;.
- If a type cannot be modified, deconstruction can be added via extension methods:
- Records
- For
recordtypes with a primary constructor, the compiler automatically synthesizes aDeconstructmethod withoutparameters for each positional parameter:public record House(int SquareFeet, string Address); var house = new House(1000, "123 Coder St."); var (sqft, addr) = house; // uses synthesized Deconstruct - Only positional properties are included in the synthesized
Deconstruct. - A custom
Deconstructcan be added to change what gets deconstructed or to provide additional shapes.
- For
- Discards
- Unneeded values can be ignored using
_:var (first, _, city, _) = person;
- Unneeded values can be ignored using
- How the compiler chooses what to call
- When the left side is a tuple-like pattern, e.g.
(a, b) = E;, the compiler deconstructsEas follows:- If
Eis already a tuple expression withnelements, use its elements. - Else if
Ehas tuple type(T1, …, Tn), use itsItem1…Itemn. - Else if
E.Deconstruct(out …)resolves to a unique instance or extension method withnoutparameters, call that method. - Otherwise,
Ecannot be deconstructed.
- If
- When the left side is a tuple-like pattern, e.g.
- Use cases
- Returning multiple values from a method in a readable way:
(int min, int max) GetRange(...) => (...); var (min, max) = GetRange(...); - Extracting multiple properties from an object in one statement instead of multiple property accesses.
- Pattern matching and
switchexpressions often combine with deconstruction for concise code.
- Returning multiple values from a method in a readable way:
In summary, deconstruction is a language feature that:
- Uses tuple syntax on the left-hand side of an assignment.
- Is backed by tuples or
Deconstruct(out ...)methods (instance or extension). - Is built into tuples and records and can be added to any user-defined type, as in the
Pointexample.
References: