反思:如何推断方法的泛型类型参数?

时间:2021-05-29 01:56:22

标签: c# reflection

在 C# 中,您可以使用如下代码通过反射调用泛型方法:

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]);

我想做一些更复杂的事情:泛型参数推断,或者类似于重载解析的东西。我想要一个 Resolve 方法来计算泛型类型参数以构造一个实例化的泛型方法:

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)));

如果它使事情更简单,对于我的应用程序,我只需要基于单个参数来解析这些类型参数(尽管目标方法实际上需要两个参数)。

我为什么要问这个问题

我正在编写一个序列化系统,用户可以在其中“插入”特定类型的序列化程序。但是我认为用户应该能够提供可以处理像 UserType<X,Y> 这样的类型的通用方法。所以我的系统将有一个通用方法列表(来自多个用户定义的类),我需要弄清楚在给定参数类型的情况下实际上可以调用哪一个方法:

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;
}

然后我将通过 Delegate.CreateDelegate(..., resolved) 构造一个委托,并使用其他一些技巧来缓存委托并像普通方法一样调用它以实现良好的性能,因为同一个委托经常被重新用于序列化或反序列化数以千计的对象。

0 个答案:

没有答案