Why does IParseable<T>
place the T : IParseable<T>
constraint on T
? What is this recursive constraint needed for?

- 102,774
- 10
- 95
- 132

- 131
- 6
-
1That is, if my T implements `IParseable
` then my T has to implement `IParseable – Marcin Aug 18 '22 at 21:07` -
6Please don't paste code as images. Always paste as text. – Enigmativity Aug 18 '22 at 23:25
1 Answers
This is so called Curiously_recurring_template_pattern (CRTP) and as far as I understand it is not strictly required (and actually it can't enforce the "correct" behaviour) but as mentioned in the Preview Features in .NET 6 – Generic Math article it's usage is a hint to support one of the quite important scenarios for static abstract interface members calls - using it via generic interfaces:
This general pattern is sometimes referred to as the Curiously Recurring Template Pattern (CRTP) and is key to allowing the feature to work.
Let's imagine the following interface:
public interface IParseable1<TSelf>
// where TSelf : IParseable1<TSelf>
{
static abstract TSelf Parse(string s, IFormatProvider? provider);
static abstract bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out TSelf result);
}
and next method:
static T InvariantParse<T>(string s)
where T : IParseable1<T>
{
return T.Parse(s, CultureInfo.InvariantCulture);
}
If we implement next classes pair:
class MyClass1
{
}
class MyClass : IParseable1<MyClass1>
{
public static MyClass1 Parse(string s, IFormatProvider? provider)
{
throw new NotImplementedException();
}
public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out MyClass1 result)
{
throw new NotImplementedException();
}
}
Then next call will not compile:
var x = InvariantParse<MyClass>("");
And while CRTP does not prevent from "incorrect" usage (i.e. class MyClass1 : IParsable<MyClass1>
will allow class MyClass : IParsable<MyClass1>
, as far as I remember there is no language construct to enforce such behaviour) it is heavily hinting on the desired usage.
Note that class MyClass : IParsable<MyClass1>
can be used in similar method but it becomes quite clumsy (partly due to specifics of C# generic types inference):
public static TOut InvariantParseTwoGenerics<T, TOut>(string s)
where T : IParseable1<TTO>
{
return T.Parse(s, CultureInfo.InvariantCulture);
}
var x1 = InvariantParseTwoGenerics<MyClass, MyClass1>("");
Post from Stephen Cleary discussing CRTP.
UPD
There was a breaking change between .NET 6 and .NET 7 - System.IParseable
was renamed to System.IParsable

- 102,774
- 10
- 95
- 132