C#使用System.Type作为通用参数


87

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

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

Session.Linq<MyType>()

但是我没有MyType,但是我想改用Type。

我所拥有的是:

System.Type typeOne;

但是我不能执行以下操作:

Session.Linq<typeOne>()

如何使用类型作为通用参数?

Answers:


95

您不能直接这样做。泛型的目的是提供编译时类型安全性,您可以在编译时知道自己感兴趣的类型,并且可以使用该类型的实例。在您的情况下,您仅知道,Type因此您无法进行任何编译时检查,以确保您拥有的任何对象都是该类型的实例。

您需要通过反射调用该方法-类似于:

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

如果您需要大量使用此类型,则可能会发现编写自己的泛型方法(调用它所需的任何其他泛型方法),然后通过反射调用的泛型方法更为方便。


1
我阅读了有关使用反射调用该方法的解决方案的信息。但我希望还有其他解决方案。
1

invoke方法返回一个“对象”。在将其强制转换为正确的Type之前,我无法查询该对象。(可能是IQueryable <T>)。如何将对象转换为我拥有的类型?
1月

3
@Jan:您不能-但是那样您也将无法使用该类型,因为您在编译时不知道该类型...这是您值得编写通用方法的地方,确实你在一个强类型的方式想要的一切,并呼吁与反思。或者,非泛型IQueryable会满足您的需求吗?
乔恩·斯基特

2
@Jon:谢谢,我将尝试编写自己的通用方法。不幸的是,非通用的Iqueryable无法解决问题。
1月

1
@Jon:使用我自己的泛型方法调用另一个泛型方法解决了该问题
1

30

为此,您需要使用反射:

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

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

如果Session实际上是object,则需要知道该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;
        }
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.