NDepend Blog

Improve your .NET code quality with NDepend

The .NET 7.0 IParsable<TSelf> interface

May 7, 2024 3 minutes read

Parsable of TSelf interface - Copy

As I explained in the post C# 11 static abstract members, C# 11 let’s write static abstract members in an interface. This feature was mostly introduced to implement the new .NET 7.0 generic math library, also explained in this article.

Introduction to the Interface IParsable<TSelf>

However static abstract members in an interface open the door to a whole new range of syntax possibilities. One of such opportunity is generic parsing through the interface System.IParsable<TSelf> introduced with .NET 7.0:

Thanks to this interface and its static abstract methods, it is now possible to have a generic extension method that parses a string to anything. Here is an example of such Parse<T>() method with a class Person implementing IParsable<Person> :

Notes on IParsable<T>

As the code sample above suggests, in .NET 7.0 IParsable<T> is implemented by all numeric types Int32, Byte, Double… but also by other types usually subject to parsing like DateTime, DateOnly or Guid.

One limitation of these new parsing APIs is that it assumes that the entire string is the value. You can easily provide your own parsing interfaces that take the start index as input and return the number of characters consumed. This way one can chain parsing operations on a string while avoiding allocating new strings through calls to SubString() :

Alternatively, .NET 7.0 provides a similar interface ISpanParsable<T> to parse from any ReadOnlySpan<Char> that typically represents a sub-string.

Generic Parsing before .NET 7.0

Just think about it: there was no easy way to implement generic parsing before .NET 7.0 and C# 11. One could have used Reflection as shown in the code sample below:

But this implementation is slower because of reflection cost. And one certainly doesn’t want a performance penalty each time a parsing occurs.

Even worse, the compile-time safety is not here to prevent calling Parse<T>() on a type T that has no Parse(string):T method.

Generic Factory Pattern

Generic parsing is an example of the Generic Factory Pattern made possible with C#11 static abstract. We can now have in our code base sweet interfaces like IParsebleFromJson or IParsebleFromXml that promote abstraction and reuse through generic algorithms.

But parsing is not the only use-case of generic factories. The code sample below proposes an extension method BuildVector<TNumeric>() that builds a vector from an array of numerics:

The Curiously Recurring Template Pattern (CRTP)

Actually the syntax interface IParsable<TSelf> where TSelf : IParsable<TSelf> is known in C++ as the Curiously Recurring Template Pattern (CRTP). This syntax allows an interface to declare methods with parameters or return type typed with the concrete type that implements the interface (like Parse(....) that returns a TSelf).

However, there is no syntax to enforce that TSelf refers to the type that actually implements the interface. In the screenshot below we can see that class Person : IParsable<Employee> compiles and runs properly. This is why the new Roslyn Analyzer CA2260 emits a warning in this situation.

Curiously Recurring Template Pattern

Conclusion

The IParsable<TSelf> interface, introduced in .NET 7.0, may seem like a minor addition, but it’s the tip of the iceberg revealing a new expanse of convenient syntaxes rooted in static abstraction.

 

Comments:

  1. Awesome – great explanations and insights. Could you offer a thought on how to best wrap unit testing around these?

  2. As with all generic impl, these new syntaxes make testing easier since not necessarily all generic types parameter consumed at runtime have to be tested. However if you care for 100% testing coverage (and you should IMHO) all parsing implementation should be thoroughly tested.

Leave a Reply

Your email address will not be published. Required fields are marked *