【问题标题】:Dynamically inferring the type parameters of a generic method from the arguments从参数动态推断泛型方法的类型参数
【发布时间】:2014-03-27 15:07:16
【问题描述】:

函数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);
}

【问题讨论】:

    标签: c# generics reflection types inference


    【解决方案1】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-11
      • 1970-01-01
      • 2016-11-01
      相关资源
      最近更新 更多