January 25, 2017 // By Jason Bock
With the addition of tuples in C#7 comes the ability to deconstruct them into their constituent parts. In this article, you’ll see how far you can push this feature to allow any type to be deconstructed.
The Basics of Deconstruction
What is deconstruction? It’s the ability to split a tuple’s values up and put them into variables. For example, here’s how you take a tuple with a
int and deconstruct them into variables:
This is beneficial if a method returns a tuple that didn’t name the tuple’s values. Instead of doing this (which leaves your code base with names like
You can deconstruct the tuple into variables with good names:
For the underlying tuple type in C#7,
System.ValueTuple, deconstruction comes for free because the compiler knows how to take that tuple and deconstruct its data into the target variables. If you want your classes to support deconstruction, you have to provide an implementation of a deconstruction method yourself. This method must be called
Deconstruct() and it must exist either as an instance method on the source type or an extension method that takes the source type as the first parameter (i.e. the
this parameter). For example, here’s an example of a type with
Deconstruct() defined on the type:
The compiler will end up calling the
Deconstruct() method on the line of code after
person has been created.
If you don’t own the type definition, you can add an extension method to support deconstruction:
This is how the original
System.Tuple type, added to .NET in 4.0, supports deconstruction. The TupleExtensions static class has a number of
Deconstruct() methods so you can deconstruct these “old” tuples:
As I started playing with tuples in C#7, I started to wonder if you could deconstruct an anonymous type, which feels a lot like a tuple. Anonymous types have been around for a while in C# - they’re really easy to create:
Unfortunately, anonymous types have limited use. They can only be used in the method that they are declared in - they can’t be returned from the method. Given the flexibility of tuples in C#7, anonymous types may end up being used less and less in .NET applications. That said, anonymous types are widely used in current C# code bases, and they have a similar structure to tuples – they’re just a class with properties that map to the names and types of values used when the anonymous type was defined. In fact, here’s what that anonymous type looks like if you decompile the code using a tool like ILSpy:
This is actually pseudo-C#. The names of the class and its members are completely mangled and wouldn’t be valid in C#. Also, the anonymous type also has other methods like
GetHashCode() implemented, but the point is that you end up with a type that is truly “anonymous” and contains the values you used when the type was defined.
Note that the anonymous type is not generated with a
Deconstruct() implementation. Also, there’s no way to define a
Deconstruct() extension method for that type because you don’t know what the name is. Therefore, it looks like there’s no way to deconstruct anonymous types.
Or can you?
Well, I tried. Here’s my definition of a generic deconstruction extension method for two values:
Note that this isn’t limited to just anonymous types; it can be used for any type you find. Also, this code is clearly not defensive in its implementation. That is, I don’t check to see if the number of public, instance, readable properties on a type equals the number of out parameters to the method, and I’m also assuming that the order of the properties found by
GetProperties() matches the order defined with the generic types in the parameters. This could easily break if
@this has 3 properties and I defined another
Deconstruct() method with 4
out parameters, or one of the properties was a type that could not be cast into the desired type specified by the developer with the generic type value. Just ignore these issues for now. We’re more interested to see if the compiler will even let us use this…but unfortunately, it won’t. If we try to use it:
Sad to say, but the compiler rejects it with CS0411: “The type arguments for method 'ObjectExtensions.Deconstruct<T1, T2>(object, out T1, out T2)' cannot be inferred from the usage. Try specifying the type arguments explicitly.” I tried declaring the variables used for deconstruction with explicit types but that doesn’t help at all.
This definition will work:
But this is downright ugly to me. Now everything is an
object and I’ll end up having casts and boxing with my deconstructed variables.
Is there any hope? Well, there’s one way to do it, but it requires an intermediately tuple object to be made, and you can’t use the magic the C# compiler adds to automatically call a
Deconstruct() method for you. Actually, it will use that magic, but you have to call an extension method first. Here’s the solution:
To() extension method lets you stuff the property values you found on the given object into a tuple, and it returns that tuple. Then C# will see that you’re trying to deconstruct that tuple into individual variables, and the deconstruction magic kicks in:
Now, while this will work, it’s not desirable for two reasons. First, the implementation of
To() has to use Reflection, and as I mentioned before, even if you make
To() defensive, it’s still fragile. If you give
To() an object with less properties than
out parameters, or if the types don’t match, it’ll fail. Even if those conditions hold, there’s no guarantee you’re going to map the right property values into the right variables. For example, the targeted type may have three properties typed as a
string. How will you know which one will get mapped into which
out parameter? The best you can do is rely on a heuristic. Second, if you really want to deconstruct a type, either put a
Deconstruct() method on the type itself, or write the extension method yourself if you don’t own the code that defines the type. They’re easy to create and write tests for.
In this article, you saw how deconstruction is done in C#7. You learned how to create your own deconstruction methods and how far you could go with that to make a generic deconstruction method. While that generic approach isn’t desirable, it’s still educational (and fun!) to play with language features to understand how they work. That knowledge makes you a better developer. Until next time, happy coding!