One of the basic design patterns in .NET is the static Parse function. Virtually every class that can be instantiated from a string representation has one. Yet before C# 11 and .NET 7, there was no way to create an interface that accommodated this.
The fundamental problem is before now, abstract interfaces (i.e. the interface keyword) didn’t support static functions. This wasn’t just a C# limitation, the CLR itself didn’t support it before the introduction of the Static Abstract Methods in interfaces feature.
As with interface methods, Static Abstract Methods can have a default implementation, but there are limitations.
The static virtual and static abstract methods declared in interfaces don't have a runtime dispatch mechanism analogous to virtual or abstract methods declared in classes. Instead, the compiler uses type information available at compile time. Therefore, static virtual methods are almost exclusively declared in generic interfaces.
The example we are going to discuss today is the IParsable<TSelf> interface. It offers two methods, Parse
and TryParse
. Both accept a String
and an IFormatProvider
. To access them, you need a generic helper method such as:
static T Parse<T>(string s, IFormatProvider? provider)
where T : IParsable<T>
{
return T.Parse(s, provider);
}
You can invoke it using a type parameter.
var a = Parse<int>("4", null);
If you aren’t ready to upgrade to .NET 7, you can simulate it using reflection.
static T Parse<T>(string s, IFormatProvider? provider)
{
//error handling omitted for clarity
var type = typeof(T);
var method = type.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public, new[] { typeof(string), typeof(IFormatProvider) });
var result = (T)method!.Invoke(null, new object?[] { s, provider })!;
return result;
}
You can call this method in same fashion as you would the IParsable
version. However, the risk in this technique is there is no compile-time safety. If the type T
doesn’t have a Parse
method or that method has the wrong set of parameters, the compiler can’t detect the mistake.
For performance sensitive code, you can also consider using the ISpanParsable
interface. By using a Span
instead of a String
, memory allocations can often be avoided.