C#使用System.Type作为泛型参数

118

我有一个类型列表(System.Type),需要在数据库上查询。

对于每个类型,我需要调用以下扩展方法(它是LinqToNhibernate的一部分):

Session.Linq<MyType>()

然而我没有MyType,但我想使用一个Type。

我现在拥有的是:

System.Type typeOne;

但我无法执行以下操作:

Session.Linq<typeOne>()

我如何将类型用作泛型参数?

3个回答

119
你无法直接这样做。泛型的目的在于提供编译时类型安全性,即在编译时知道你感兴趣的类型,并可以使用该类型的实例。在你的情况下,你只知道类型,因此无法获得任何编译时检查,以确保你拥有的任何对象都是该类型的实例。
你需要通过反射调用该方法 - 类似于以下内容:
// Get the generic type definition
MethodInfo method = typeof(Session).GetMethod("Linq", 
                                BindingFlags.Public | BindingFlags.Static);

// Build a method with the specific type argument you're interested in
method = method.MakeGenericMethod(typeOne);
// The "null" is because it's a static method
method.Invoke(null, arguments);

如果你需要经常使用这种类型,你可能会发现编写自己的通用方法更方便,该方法调用它所需的任何其他通用方法,然后使用反射调用你的方法。


2
我了解到一种使用反射调用方法的解决方案。但我希望有另一种解决方案。 - Jan
invoke方法返回一个“Object”。在将其转换为正确的类型之前,我无法对此对象进行查询。 (这可能是IQueryable <T>)。我该如何将对象转换为我拥有的类型? - Jan
3
@Jan:你不能这么做,因为你在编译时不知道类型。这可能值得你编写一个强类型的通用方法,以执行你所需的一切,并使用反射调用它。或者,非泛型的 IQueryable 是否满足你的需求? - Jon Skeet
2
@Jon:谢谢,我将尝试编写自己的通用方法。不幸的是,非泛型IQueryable不能解决问题。 - Jan
1
@Jon:使用我的自定义通用方法调用另一个通用方法解决了问题。 - Jan
显示剩余3条评论

43

要做到这一点,你需要使用反射:

typeof(Session).GetMethod("Linq").MakeGenericMethod(typeOne).Invoke(null, null);

(假设Linq<T>()Session类型的静态方法)

如果Session实际上是一个对象,那么您需要知道Linq方法实际上是在哪里声明的,并将Session作为参数传递:

typeof(DeclaringType).GetMethod("Linq").MakeGenericMethod(typeOne)
     .Invoke(null, new object[] {Session});

1
我有一个通用的方法,叫做通过反射调用通用方法。
/// <summary>
/// This method call your method through Reflection 
/// so i wil call the method like CallGenericMethodThroughReflection<Session>(assemblyQualifiedName,Linq,false,new[] { file }) 
/// </summary>
/// <typeparam name="T">Call method from which file</typeparam>
/// <param name="assemblyQualifiedName">Your can get assemblyQualifiedName like typeof(Payroll.Domain.Attendance.AttendanceApplicationMaster).AssemblyQualifiedName</param>
/// <param name="methodName"></param>
/// <param name="isStaticMethod"></param>
/// <param name="paramaterList"></param>
/// <param name="parameterType">pass parameter type list in case of the given method have overload  </param>
/// <returns>return object of calling method</returns>
public static object CallGenericMethodThroughReflection<T>(string assemblyQualifiedName, string methodName,bool isStaticMethod ,object[] paramaterList,Type[] parameterType = null)
{
    try
    {
        object instance = null;
        var bindingAttr = BindingFlags.Static | BindingFlags.Public;
        if (!isStaticMethod)
        {
            instance = Activator.CreateInstance<T>();
            bindingAttr = BindingFlags.Instance | BindingFlags.Public;
        }
        MethodInfo MI = null;
        var type = Type.GetType(assemblyQualifiedName);
        if(parameterType == null)
            MI = typeof(T).GetMethod(methodName, bindingAttr);
        else
            MI = typeof(T).GetMethod(methodName, bindingAttr,null, parameterType, null);//this will work in most case some case not work
        if (type == null || MI == null) // if the condition is true it means given method or AssemblyQualifiedName entity not found
            return null;
        var genericMethod = MI.MakeGenericMethod(new[] { type });
        return genericMethod.Invoke(instance, paramaterList);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接