Equip 3rd party types with a deconstructor

08/04/2024

Deconstructor are a C# language feature that allows you to define a method that will be called when an object is being split up into its components. While this is straightforward to implement for your own types, it is not possible to add a deconstructor to a 3rd party type - or is it?

Deconstructor

Deconstructors are a C# language feature that allows you to define a method that will be called when an object is being split up into its components. Here is a very simple example:

public class Point
{
    public int X { get; }
    public int Y { get; }

    public Point(int x, int y)
    {
        X = x;
        Y = y;
    }

    public void Deconstruct(out int x, out int y)
    {
        x = X;
        y = Y;
    }
}

Which can then be called like this:

var point = new Point(3, 4);
var (x, y) = point;

Nice and easy, but your method needs the Deconstruct method to be defined. What if you want to deconstruct a 3rd party type that you cannot modify? Well, as many other features in C#, the compiler is your friend and can help you out. How? The compiler will look for a method with the signature Deconstruct in the type you are trying to deconstruct, but it will also look for an extension method with the same signature. This means that you can define a deconstructor for a 3rd party type by defining an extension method with the same signature. Here is an example:

public static class Extensions
{
    public static void Deconstruct(
        this Uri uri, 
        out string scheme,
        out string host,
        out string path,
        out string query,
        out string fragment)
    {
        scheme = uri.Scheme;
        host = uri.Host;
        path = uri.AbsolutePath;
        query = uri.Query;
        fragment = uri.Fragment;
    }
}

And then you can use it like this:

const string url = "https://steven-giesel.com/1?query=test#anchor";
var (scheme, host, path, query, fragment) = new Uri(url);

Console.WriteLine($"Scheme: {scheme}"); // https
Console.WriteLine($"Host: {host}"); // steven-giesel.com
Console.WriteLine($"Path: {path}"); // /1
Console.WriteLine($"Query: {query}"); // ?query=test
Console.WriteLine($"Fragment: {fragment}"); // #anchor

This is a more niche feature, and it is more convenient than a necessity. However, if you have a fixed set of properties you need from a type, this can be a very concise way to get them.

Shorts: foreach on type without IEnumerable

Is it possible to have a foreach loop over a type which does not implement IEnumerable? Yes it is.

Time abstraction in .NET 8

With the upcoming release of .NET, the team introduced an abstraction of time itself. That can bring you major benefits especially if you have to test scenarios where time is a crucial part! Until now, you had to create your own wrapper. This, of course, makes integration with 3rd party libraries tricky.

C# Lowering

Did you ever hear the word "compiler magic" or "syntactic sugar"? Probably yes and therefore we want to dissect what this "magic" really is!

We can see how we can predict performance or bugs by "lowering" our code. Also we will see how things like foreach, var, lock, using, async, await, yield, anonymous types, record, stackalloc, pattern matching, Blazor components, deconstructor, extension methods... do not really exist.

An error has occurred. This application may no longer respond until reloaded. Reload x