4

.NET 7 recently introduced IParsable as an interface, and I'd like to check for its presence. Some test that will return true if T implements IParsable<T> and false otherwise.

Say I want to return an object that is of type T when T has a Parse method, for example:

T ParseAs<T>(string s)
{
    if (typeof(IParsable<T>).IsAssignableFrom(typeof(T)))
    {
        return T.Parse(s);
    }
    else
    {
        //do something else...
    }
}

I would hope this to check if T implements IParsable<T> and grant me access to the Parse and TryParse methods inside. I don't seem to be able to use T as a type argument for IParsable, instead receiving this exception:

CS0314
The type 'T' cannot be used as type parameter 'TSelf' in the generic type or method 'IParsable<TSelf>'. There is no boxing conversion or type parameter conversion from 'T' to 'System.IParsable<T>'

I also receive the above error if I try to use is:

s is IParsable<T>

How would I resolve this?

GSerg
  • 76,472
  • 17
  • 159
  • 346
Danatron1
  • 43
  • 3
  • 2
    Does this help? https://stackoverflow.com/a/503359/1974021 – DasKrümelmonster Nov 19 '22 at 17:20
  • That compiles, but doesn't allow me to do T.Parse(s) - do you know why that is? – Danatron1 Nov 19 '22 at 17:27
  • 1
    `IParsable` is [recursively defined](https://stackoverflow.com/a/73409612/11683), so you need to establish that `T` is `IParsable` *before* you could say `is IParsable`... – GSerg Nov 19 '22 at 17:45
  • Am I understanding you correctly in saying that before I can check if T is IParsable, I have to know that T is IParsable? That sounds like a catch-22. – Danatron1 Nov 19 '22 at 17:50

1 Answers1

4

To be able to use T.Parse() syntax - you need to know at compile time that T implements IParseable<T>. The only way you can be sure about that at compile time is to explicitly say that:

T ParseAs<T>(string s) where T: IParsable<T> {
    return T.Parse(s, null);
}

If you have just type T without explicitly saying it's IParsable<T> - it's not possible to use T.Parse syntax and you should use reflection all the way. That is first you check if T implements that interface via reflection, and then you again use reflection to call that static method:

T ParseAs<T>(string s) {
    var isParsable = typeof(T).GetInterfaces().Any(c => c.IsGenericType && c.GetGenericTypeDefinition() == typeof(IParsable<>));
    if (isParsable) {
        var parse = typeof(T).GetMethods(BindingFlags.Static | BindingFlags.Public)
            .FirstOrDefault(c => c.Name == "Parse" && c.GetParameters().Length == 2 && c.GetParameters()[0].ParameterType == typeof(string) && c.GetParameters()[1].ParameterType == typeof(IFormatProvider));
        if (parse != null)
            return (T) parse.Invoke(null, new object[] { s, null });
    }

    return default(T);
}

That's of course pretty ugly and you'll likely won't want to do that.

Evk
  • 98,527
  • 8
  • 141
  • 191
  • The reflection approach was the one I was using prior to this update, and I was looking to replace it with something more elegant. Thank you for the answer - it exactly answers my question. – Danatron1 Nov 19 '22 at 18:57