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;
        }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.