1

该函数MethodInfo.MakeGenericMethod要求您传递与类型参数对应的类型数组。我处于类型签名非常重要且直到运行时才知道的情况。我只有函数的参数类型。

因此,例如,这是一个函数签名:

static IArray<U> Map<T, U>(IArray<T> xs, Func<T, U> fxn) { ... }

我有两个类型为 t1(例如 IArray)和 t2(例如 Func)的参数仅在运行时才知道。我想在运行时利用 C# 类型推断算法,例如从 t1 和 t2 计算 T 和 U。

MethodInfo MakeGenericMethodUsingInference(MethodInfo mi, Type t1, Type t2) {
   var typeArg1 = ??;
   var typeArg2 = ??;
   return mi.MakeGenericMethod(typeArg1, typeArg2);
}
4

1 回答 1

0

现在我有一个手动解决方法,可以很好地满足我的需求,但感觉必须有更好的方法:

    public class TypeArgumentInferenceEngine
    {
        public Dictionary<Type, Type> ParameterToConcrete = new Dictionary<Type, Type>();

        public void MatchTypes(Type concrete, Type generic)
        {
            if (generic.IsGenericParameter)
            {
                if (ParameterToConcrete.ContainsKey(generic))
                {
                    var tmp = ParameterToConcrete[generic];
                    if (!tmp.Equals(concrete))
                        throw new Exception(String.Format("Template parameter {0} is inconsistent between {1} and {2}", generic, concrete, tmp));
                }
                else
                {
                    ParameterToConcrete.Add(generic, concrete);
                }
            }
            else if (generic.IsGenericType) 
            {
                if (!concrete.IsGenericType)
                    throw new Exception("Expected generic concrete type");
                var concreteTypeArgs = concrete.GetGenericArguments();
                var genericTypeArgs = generic.GetGenericArguments();
                if (concreteTypeArgs.Length != genericTypeArgs.Length)
                    throw new Exception("Mismatched number of generic argument types");
                for (int i = 0; i < genericTypeArgs.Length; ++i)
                    MatchTypes(concreteTypeArgs[i], genericTypeArgs[i]);
            }
        }

        public static Dictionary<Type, Type> InferTypeParameters(Type[] concreteTypes, Type[] genericTypes)
        {
            var engine = new TypeArgumentInferenceEngine();
            int n = concreteTypes.Length;
            if (n != genericTypes.Length) throw new ArgumentException("Both input arrays have to be the same size");
            for (int i = 0; i < n; ++i)
                engine.MatchTypes(concreteTypes[i], genericTypes[i]);
            foreach (var t in engine.ParameterToConcrete.Keys)
                if (!t.IsGenericParameter)
                    throw new Exception("Expected a generic type parameter");
            return engine.ParameterToConcrete;
        }

        public static MethodInfo MakeGenericMethodFromArgTypes(MethodInfo mi, params Type[] argTypes)
        {
            if (!mi.IsGenericMethodDefinition)
                return mi;
            var typeParams = mi.GetGenericArguments();
            var paramTypes = mi.GetParameters().Select(p => p.ParameterType).ToArray();
            var typeLookup = InferTypeParameters(argTypes, paramTypes);
            var typeArgs = new List<Type>();
            foreach (var p in typeParams) 
            {
                if (!typeLookup.ContainsKey(p))
                    throw new Exception(String.Format("Type parameter {0} is not bound: ", p));
                typeArgs.Add(typeLookup[p]);
            }
            return mi.MakeGenericMethod(typeArgs.ToArray());
        }
    }

    public static void TestTypeDeduction()
    {
        var t1 = typeof(IArray<int>);
        var t2 = typeof(Func<int, bool>);

        // Map has the type "Func<IArray<T> xs, Func<T, U>, IArray<U>>"
        var genericMethod = typeof(Ops).GetMethod("Map");
        var concreteMethod = TypeArgumentInferenceEngine.MakeGenericMethodFromArgTypes(genericMethod, t1, t2);
        Console.WriteLine("{0}, {1} => {2}", t1, t2, concreteMethod.ReturnType);

        //var nonGenericMethod = genericMethod.MakeGenericMethod(typeArgs.ToArray());

    }
于 2014-03-27T17:59:10.017 回答