If you're happy to use dynamic code then you can use the technique illustrated here:
static T ParseAs<T>(string s) {
var isParsable = typeof(T).GetInterfaces().Any(c => c.IsGenericType && c.GetGenericTypeDefinition() == typeof(IParsable<>));
if (isParsable) {
return Parse(s, (dynamic)default(T));
}
return default(T);
}
static T Parse<T>(string stringValue, T defaultValue) where T : IParsable<T>
{
return T.Parse(stringValue, null);
}
Here the parameter named defaultValue
exists purely to allow the compiler to perform type inference at runtime using the dynamic type.
If you're curious as to how this dynamic code performs compared with static invocations, or invocations by reflection, see the following benchmarks, run on .Net 8. In short, the dynamic invocation is 5 times faster than reflection, but 40 times slower than the static invocation.
Method |
Mean |
Error |
StdDev |
ParseStatic |
5.909 ns |
0.1440 ns |
0.1658 ns |
ParseDynamic |
208.928 ns |
1.5338 ns |
1.4347 ns |
ParseReflection |
1,060.491 ns |
9.5423 ns |
8.4590 ns |
using System.Reflection;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkRunner.Run<ParseBenchmark>(); ;
public class ParseBenchmark
{
[Benchmark]
public int ParseStatic() => int.Parse("1");
[Benchmark]
public int ParseDynamic() => ParseAsDynamic<int>("1");
[Benchmark]
public int ParseReflection() => ParseAsReflection<int>("1");
T ParseAsReflection<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);
}
static T ParseAsDynamic<T>(string s)
{
var isParsable = typeof(T).GetInterfaces().Any(c => c.IsGenericType && c.GetGenericTypeDefinition() == typeof(IParsable<>));
if (isParsable)
{
return Parse(s, (dynamic)default(T));
}
return default(T);
}
static T Parse<T>(string stringValue, T defaultValue) where T : IParsable<T>
{
return T.Parse(stringValue, null);
}
}
IParsable
is recursively defined, so you need to establish thatT
isIParsable<T>
before you could sayis IParsable<T>
... – Tenebrific