static abstract interfaces and generic math

10/17/2021
3 minute read

In case you missed the announcment. here it is; Preview Features in .NET6. What does it do? Well we now have the ability to declare static abstract function as an contract either on your interfaces.

By the way: To add preview features is a very nice way to get early feedback. I like the approach very much.

Let's go

Where does it come in handy? You remember all those Parse or TryParse method on some value types. Just imagine you want to have that on your own data type. Now you can do it:

Setup

Before we dive in, we have to enable the preview features in our csproj file. Add <EnablePreviewFeatures>true</EnablePreviewFeatures> and <LangVersion>preview</LangVersion> to any PropertyGroup. For example

<PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <LangVersion>preview</LangVersion>
    <EnablePreviewFeatures>true</EnablePreviewFeatures>
</PropertyGroup>

You can use Rider 2021.2 or Visual 2019 but be aware, that even though the code will compile your IDE will go havoc on you as it doesn't know the feature. For best experience either use Visual Studio 2022 Preview or Rider EAP.

Example

public interface IParseable<T>
{
    static abstract T Zero { get; }
    static abstract T Parse(string from);
    static abstract bool TryParse(string from, out T obj);
}

Now if we derive from that interface we have to implement all those static functions as well:

public class MyBetterGuid : IParsable<MyBetterGuid>
{
    private long _internalGuid;

    public static MyBetterGuid Zero => throw new NotImplementedException();

    public static MyBetterGuid Parse(string from)
    {
        throw new NotImplementedException();
    }

    public static bool TryParse(string from, out MyBetterGuid value)
    {
        throw new NotImplementedException();
    }
}

Current Design "limitations"

  • Does not work with default interface methods introduced in C# 8 (static virtual is not a valid combination)
  • Only available on interfaces and not on (abstract) classes
  • Not accessible via the interface (IParsable<MyBetterGuid>.Parse("123"); is forbidden - as they are static there is no object to invoke)

Generic Math

Now this is a huge enables for generic math. All operators like +, - are defined as static operations on a class. Now we can leverage this to introduce this on an interface level. But where is the benefit: Have a look at the following

public int Sum(IEnumerable<int> source)
{
    int sum = default;
    foreach (var s in source)
    {
        sum = sum + s;
    }

    return sum;
}

This works without any problem. So what happens if we introduce generic?

public T Sum<T>(IEnumerable<T> source) 
{
    T sum = default;
    foreach(var s in source)
    {
        sum = sum + s;
    }

    return sum;
}

Well, that doesn't compile anymore because the compiler can't apply the operator + to the operands. But why can it do so for IEnumerable<int>? Well because LINQ brings some functions with it exactly for that reason:

public static partial class Enumerable {
    public static int Sum(this IEnumerable<int> source) { ... }

But now we have the ability to make it work with static abstract interfaces! We just need to define this operator on an interface level and use it in our function:

public interface IAdditionOperators<TSelf>
where TSelf : IAdditionOperators<TSelf>
{
    static abstract TSelf operator +(TSelf left, TSelf right);
}

public class MyBetterGuid : IAdditionOperators<MyBetterGuid>
{
    public static MyBetterGuid operator +(MyBetterGuid left, MyBetterGuid right)
    {
        // ...
    }
}

And now we just use it in our sum function from above. But this time we explicitly constraining the method to our interface.

public T Sum<T>(IEnumerable<T> source) where T : IAdditionOperators<T>
{
    T sum = default;
    foreach(var s in source)
    {
        sum = sum + s;
    }

    return sum;
}

That works like a charm!

Conclusion

With the new Preview features static abstract interfaces and generic math we get some powerful tools in our hands to write code. Also I love the choice of the .NET team to make them available as a preview to gather quick feedback!

Interfaces can have private methods

Let's drop some "useless" knowledge here. Interfaces can have private methods. This comes with the C# 8 feature: "Default interface methods".

Abstract class vs interface

In C# we have abstract classes and we also have interfaces. Both seem very similiar but they are fundamentally different concepts.

Therefore let's discover what the difference is, what the overlap is and when to use what.

LINQ on steroids with SIMD

In this blog post, we will explore the use of SIMD instructions to speed up LINQ queries. We will use the Vector type of performing SIMD operations on arrays of data. We will also use the BenchmarkDotNet library to measure the performance of our code. We will also see how this works hand in hand with the new "generic math" feature of C# 10.

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