C # использовать System.Type в качестве универсального параметра

88

У меня есть список типов (System.Type), которые нужно запрашивать в базе данных.

Для каждого из этих типов мне нужно вызвать следующий метод расширения (который является частью LinqToNhibernate):

Session.Linq<MyType>()

Однако у меня нет MyType, но я хочу использовать вместо него Type.

Что у меня есть:

System.Type typeOne;

Но я не могу выполнить следующее:

Session.Linq<typeOne>()

Как я могу использовать тип в качестве универсального параметра?

Янв
источник

Ответы:

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
Я читал о решении, которое использует отражение для вызова метода. Но я надеялся, что есть другое решение.
Jan
метод invoke возвращает «объект». Я не могу запросить этот объект, пока не выберу правильный тип. (Что, вероятно, будет IQueryable <T>). Как я могу привести объект к имеющемуся у меня типу?
Jan
3
@Jan: Вы не можете - но тогда вы также не сможете использовать этот тип, потому что вы не знаете тип во время компиляции ... здесь, возможно, стоит написать общий метод, который делает все, что вы хотите, строго типизированным способом и вызывая это с помощью отражения. В качестве альтернативы, делает ли необщий IQueryableто, что вам нужно?
Джон Скит,
2
@Jon: Спасибо, я попробую написать свой собственный общий метод. К сожалению, неуниверсальный Iqueryable не решит проблему.
Jan
1
@ Джон: используя свой собственный общий метод для вызова другого шаблонного метода решить эту проблему
Jan
30

Для этого вам нужно использовать отражение:

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;
        }
    }
Калпеш Дабхи
источник