Is there a way to get typeof Func?

2020-06-03 08:44发布

问题:

Short version :

We can get the typeof Func<T,T> using:

typeof(Func<,>) 

but what if I want to get the type of Func<T, bool>, what should I use, or is it possible to do? Clearly this doesn't compile :

typeof(Func<, bool>)

Long version :

Consider the following scenario, I have two similar method and I want to get the second one (Func<T, int>) using Reflection:

public void Foo<T>(Func<T, bool> func) { }

public void Foo<T>(Func<T, int> func) { }

I'm trying this:

 var methodFoo = typeof (Program)
            .GetMethods()
            .FirstOrDefault(m => m.Name == "Foo" &&
                        m.GetParameters()[0]
                        .ParameterType
                        .GetGenericTypeDefinition() == typeof (Func<,>));

But since the generic type definition of Func<T, bool> and Func<T, int> are equal it gives me the first method. To fix this I can do the following:

var methodFoo = typeof (Program)
            .GetMethods()
            .FirstOrDefault(m => m.Name == "Foo" &&
                        m.GetParameters()[0]
                        .ParameterType
                        .GetGenericArguments()[1] == typeof(int));

Then I get the correct method but I do not like this way. And it seems like a overhead for more complex situations. What I want to do is get the type of Func<T,bool> like in my failed attempt above, then instead of using Linq I can use this overload of GetMethod and do something like the following:

var methodFoo = typeof (Program)
            .GetMethod("Foo", 
            BindingFlags.Public | BindingFlags.Instance,
            null, 
            new[] {typeof (Func<, bool>)}, // ERROR typeof(Func<,>) doesn't work either
            null);

Note: Ofcourse Func<T,T> is just an example, question is not specific to any type.

回答1:

Unfortunately, you cannot build a System.Type object for a partially bound generic type. The way that you do it (i.e. with GetGenericArguments()[1] == typeof(int)) is the proper way of doing it.

If you need to re-use this in multiple places, you could build a helper extension method that takes a generic type definition and an array of System.Type objects, and returns true if there is a match:

static bool IsGenericInstance(this Type t, Type genTypeDef, params Type[] args) {
    if (!t.IsGenericType) return false;
    if (t.GetGenericTypeDefinition() != genTypeDef) return false;
    var typeArgs = t.GetGenericArguments();
    if (typeArgs.Length != args.Length) return false;
    // Go through the arguments passed in, interpret nulls as "any type"
    for (int i = 0 ; i != args.Length ; i++) {
        if (args[i] == null) continue;
        if (args[i] != typeArgs[i]) return false;
    }
    return true;
}

Now you can rewrite your code like this:

var methodFoo = typeof (Program)
    .GetMethods()
    .FirstOrDefault(m => m.Name == "Foo" &&
        m.GetParameters()[0]
            .ParameterType
            .IsGenericInstance(typeof(Func<,>), null, typeof(bool))
    );

if I use methodFoo.GetParameters()[0].ParameterType, I'm getting the type of Func<T, int> so it is definitely being constructed somewhere

The type T above is the generic type parameter of your generic method Foo. Since it is not "any type", you could construct this type if you wish:

var typeT = methodFoo.GetGenericArguments()[0];
var funcTbool = typeof(Func<,>).MakeGenericType(typeT, typeof(bool));

The catch is that typeT is bound to the specific generic method, making the funcTbool type not suitable for searching across multiple independent generic methods.

If T were a type argument of the class to which the method belongs, say

class FooType<T> {
    public void Foo(Func<T, bool> func) { }
    public void Foo(Func<T, int> func) { }
}

you would be able to construct a funcTbool based on FooType<>'s generic type parameter, and search for it in the signatures of the different Foo(...) methods.