In C# you can call a generic method via reflection with code like this:
public class Sample {
public void Foo<T>() { Console.WriteLine(typeof(T).Name); }
}
MethodInfo method = typeof(Sample).GetMethod(nameof(Sample.Foo));
MethodInfo generic = method.MakeGenericMethod(typeof(int));
generic.Invoke(new Sample(), new object[0]);
I want to do something more complicated: generic parameter inference, or something similar to overload resolution. I want a Resolve
method that figures out the generic type parameters in order to construct an instantiated generic method:
public static MethodInfo Resolve(MethodInfo genericMI, params Type[] argTypes)
{
// for example, if genericMI is a reference to
// `Sample.Foo2<,>` (below) and argTypes is
// `new[] { typeof(List<(int, float)>) }`, this
// method would return a `MethodInfo` for
// `Sample.Foo2<int, float>`. But how?
}
public class Sample {
public void Foo1<T>(IEnumerable<T> data) { }
public void Foo2<T, U>(IEnumerable<(T, U)> data) { }
public void Foo3<T, U>(IEnumerable<U> list, (T, U) tuple) where U: struct { }
}
// EXAMPLES:
// Returns MethodInfo for Foo1<(int, float)>
var foo2 = Resolve(typeof(Sample).GetMethod("Foo1"), typeof(List<(int, float)>));
// Returns MethodInfo for Foo2<int, float>
var foo2 = Resolve(typeof(Sample).GetMethod("Foo2"), typeof(List<(int, float)>));
// Returns MethodInfo for Foo3<int, byte>
var foo3 = Resolve(typeof(Sample).GetMethod("Foo3"), typeof(List<byte>), typeof((int, byte)));
// Returns null
var failA = Resolve(typeof(Sample).GetMethod("Foo3"), typeof(List<byte>), typeof((int, float)));
// Returns null
var failB = Resolve(typeof(Sample).GetMethod("Foo3"), typeof(List<Regex>), typeof((int, Regex)));
If it makes things simpler, for my application I only need to resolve these type parameters based on a single parameter (although the target method actually takes two parameters).
Why I'm asking this question
I'm writing a serialization system in which users can "plug in" serializers for specific types. But I think users should be able to provide generic methods that can handle families of types like UserType<X,Y>
. So my system will have a list of generic methods (from multiple user-defined classes), and I need to figure out which one of the methods is actually possible to call given the parameter type:
public static MethodInfo Resolve(MethodInfo[] genericMIs, params Type[] argTypes)
{
foreach (var mi in genericMIs) {
var resolved = Resolve(mi, argTypes);
if (resolved != null)
return resolved;
}
return null;
}
I will then construct a delegate via Delegate.CreateDelegate(..., resolved)
and use some other tricks to cache the delegate and invoke it like a normal method to achieve good performance, as the same delegate will often be re-used to serialize or deserialize thousands of objects.
Aucun commentaire:
Enregistrer un commentaire