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!