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

Как лучше всего вызвать универсальный метод, когда параметр типа неизвестен во время компиляции, но вместо этого получается динамически во время выполнения?

Рассмотрим следующий пример кода - какой самый краткий способ вызвать Example() внутри метода GenericMethod<T>() с помощью Type, хранящегося в переменной myType?

public class Sample
{
    public void Example(string typeName)
    {
        Type myType = FindType(typeName);

        // What goes here to call GenericMethod<T>()?
        GenericMethod<myType>(); // This doesn't work

        // What changes to call StaticMethod<T>()?
        Sample.StaticMethod<myType>(); // This also doesn't work
    }

    public void GenericMethod<T>()
    {
        // ...
    }

    public static void StaticMethod<T>()
    {
        //...
    }
}

Я попробовал решение Джона и не смог заставить его работать, пока не сделал общий метод общедоступным в своем классе. Я знаю, что другой Джон ответил, что вам нужно указать флаги привязки, но это не помогло.

naskew 14.06.2012 11:31

Вам также понадобится BindingFlags.Instance, а не только BindingFlags.NonPublic, чтобы получить частный / внутренний метод.

Lars Kemmann 15.02.2013 23:11

Современная версия этого вопроса: stackoverflow.com/q/2433436/103167

Ben Voigt 12.09.2014 20:38

@Peter Mortensen - кстати, я использовал пробелы перед '?' чтобы отделить английские части от неанглийских (C#) частей; ИМХО удаление пробела делает его похожим на? является частью кода. Если бы не было кода, я бы конечно согласился удалить пробелы, но в этом случае ...

Bevan 28.11.2015 23:09

Мы можем определить универсальный метод, а затем использовать метод GetMethod, чтобы получить всю информацию об универсальном методе и использовать ее.

elnaz jangi 12.06.2020 22:34
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1 127
5
281 153
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Вам нужно использовать отражение, чтобы начать с метода, а затем «сконструировать» его, указав аргументы типа с помощью MakeGenericMethod:

MethodInfo method = typeof(Sample).GetMethod(nameof(Sample.GenericMethod));
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Для статического метода передайте null в качестве первого аргумента в Invoke. Это не имеет ничего общего с универсальными методами - это просто нормальное отражение.

Как уже отмечалось, многое из этого проще, чем в C# 4 с использованием dynamic - если, конечно, вы можете использовать вывод типов. Это не помогает в тех случаях, когда вывод типа недоступен, например, в точном примере в вопросе.

+1; обратите внимание, что GetMethod() по умолчанию рассматривает только общедоступные методы экземпляра, поэтому вам может потребоваться BindingFlags.Static и / или BindingFlags.NonPublic.

user565869 17.12.2011 02:32

Правильная комбинация флагов - BindingFlags.NonPublic | BindingFlags.Instance (и, возможно, BindingFlags.Static).

Lars Kemmann 15.02.2013 23:10

@LarsKemmann: Почему NonPublic, когда желаемый метод является общедоступным?

Jon Skeet 16.02.2013 02:08

Извините, я должен был указать; это было в ответ на комментарий @Jon of All Trades о том, как получить доступ к непубличным методам. Ваш ответ, конечно же, правильный, без каких-либо привязок для общедоступных методов.

Lars Kemmann 17.02.2013 09:00

Вопрос, который становится помеченным как обман, заключается в том, как это сделать с помощью статических методов - и технически вопрос здесь. Первый параметр generic.Invoke () должен иметь значение null при вызове статических методов. Первый параметр необходим только при вызове методов экземпляра.

Chris Moschini 23.03.2013 01:30

@ChrisMoschini: Добавил это к ответу.

Jon Skeet 23.03.2013 01:32

Этот метод является излишним для многих случаев использования, использование dynamic может решить множество проблем без использования отражения. См. Ответ @Mariusz (3-й).

gwenzek 24.04.2015 12:38

@gzou: Теперь это правда, да. Хотя посмотрите на дату вопроса и ответьте ...

Jon Skeet 24.04.2015 12:41

@JonSkeet Да, просто говорю это людям, прибывающим сюда из Google, таким как я.

gwenzek 24.04.2015 12:43

@gzou: Я кое-что добавил к ответу, но обратите внимание, что для вызова общих методов в вопросеdynamic не помогает, потому что вывод типа недоступен. (Нет аргументов, которые компилятор может использовать для определения аргумента типа.)

Jon Skeet 24.04.2015 12:44

@JonSkeet, ты можешь сделать это функцией?

Demodave 02.06.2015 17:07

@Demodave: Я не уверен, что вы имеете в виду - просто оберните этот код в метод? Вы пробовали делать это самостоятельно?

Jon Skeet 02.06.2015 17:30

@JonSkeet Это не работает, если есть 2 метода с одинаковым именем, один общий и один не общий, пример находится в классе DbContext EntityFramework: общедоступный виртуальный DbSet <TEntity> Set <TEntity> () где TEntity: class; общедоступный виртуальный набор DbSet (Тип entityType); Вызов typeof (DbContext) .GetMethod ("Set") приводит к исключению AmbigiousMatchException.

Salman Hasrat Khan 11.06.2015 12:14

@SalmanHasratKhan: Это сработает, если у вас есть правильный метод для начала - вам просто нужно вызвать GetMethods и найти правильный метод, основанный на том, что вы о нем знаете. Вызов MakeGenericMethod будет таким же.

Jon Skeet 11.06.2015 12:33

@JonSkeet, это правильно. Мне пришлось использовать GetMethods с запросом, чтобы убедиться, что свойство IsGeneric метода истинно, чтобы выбрать правильный метод.

Salman Hasrat Khan 11.06.2015 12:35

@SalmanHasratKhan: Верно. Вам нужно было бы сделать что-то подобное, если бы у вас был метод, перегруженный другими способами, даже если все методы были универсальными.

Jon Skeet 11.06.2015 12:36

@JonSkeet должен вовремя, чтобы избежать вызова, выполняющего отражение, вы можете сохранить объект в динамической переменной и после этого вызвать метод. Единственное, что компилятор не проверяет типобезопасность. Есть ли способ уточнить свой ответ на этот вопрос, если вы можете или не можете выполнить вызов универсального метода, такого как Set <TEntity>, с использованием dynamic? в этом случае TEntity должен быть статическим типом, и этот вызов недопустим. Set <runtimeobj.GetType ()>

Zinov 14.03.2018 17:15

@Zinov: Боюсь, я не совсем понимаю, о чем вы спрашиваете. Это, наверное, лучше было бы выразить в новом вопросе с примером.

Jon Skeet 14.03.2018 18:49

Также хорошей практикой будет получить такой метод: MethodInfo method = typeof (Sample) .GetMethods (). Single (method => method.Name == nameof (Sample.GenericMethod));

Lachezar Lalov 13.06.2018 11:39

Просто дополнение к исходному ответу. Пока это будет работать:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Это также немного опасно, поскольку вы теряете проверку времени компиляции для GenericMethod. Если позже вы выполните рефакторинг и переименуете GenericMethod, этот код не заметит и не сработает во время выполнения. Кроме того, если есть какая-либо пост-обработка сборки (например, обфускация или удаление неиспользуемых методов / классов), этот код тоже может сломаться.

Итак, если вы знаете метод, на который вы ссылаетесь во время компиляции, и он не вызывается миллионы раз, поэтому накладные расходы не имеют значения, я бы изменил этот код на:

Action<> GenMethod = GenericMethod<int>;  //change int by any base type 
                                          //accepted by GenericMethod
MethodInfo method = this.GetType().GetMethod(GenMethod.Method.Name);
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

Хотя это и не очень красиво, у вас есть ссылка на GenericMethod во время компиляции, и если вы проведете рефакторинг, удалите или сделаете что-нибудь с GenericMethod, этот код будет продолжать работать или, по крайней мере, сломаться во время компиляции (если, например, вы удалите GenericMethod).

Другой способ сделать то же самое - создать новый класс-оболочку и создать его через Activator. Не знаю, есть ли способ лучше.

В случаях, когда для вызова метода используется отражение, обычно само имя метода обнаруживается другим методом. Знать заранее название метода нечасто.

Bevan 28.02.2011 00:59

Что ж, я согласен с обычным использованием отражения. Но исходный вопрос заключался в том, как вызвать «GenericMethod <myType> ()». Если бы этот синтаксис был разрешен, нам вообще не понадобился бы GetMethod (). Но на вопрос «как мне написать« GenericMethod <myType> »? Я думаю, что ответ должен включать способ избежать потери связи времени компиляции с GenericMethod. Теперь, является ли этот вопрос обычным или нет, я не знаю, но Я знаю, что вчера у меня была именно эта проблема, и поэтому я столкнулся с этим вопросом.

Adrian Gallero 01.03.2011 00:24

Вы можете использовать GenMethod.Method.GetGenericMethodDefinition() вместо this.GetType().GetMethod(GenMethod.Method.Name). Он немного чище и, наверное, безопаснее.

Daniel Cassidy 10.05.2011 14:10

Что означает myType в вашем примере?

DmitryBoyko 07.12.2011 17:45

«myType» - это переменная, содержащая Тип. Если бы мы знали тип, мы могли бы выполнить GenericMethod <Type> (), но поскольку у нас есть тип в переменной, мы используем отражение.

kd8azz 14.02.2013 19:44

Теперь вы можете использовать nameof(GenericMethod)

dmigo 17.03.2016 13:02

Когда я пробую первую строку Action <> GenMethod = GenericMethod <int>; Я получаю ошибку компилятора CS7003 Неожиданное использование несвязанного общего имени для левой стороны и CS0123 Отсутствие перегрузки для GenericMethod соответствует делегату Action <T> для правой. Я использую .Net 4.5.

Eric Scherrer 16.02.2018 02:52

Как сделать Action<> GenMethod = GenericMethod<Type> в VB?

FalcoGer 20.12.2019 10:29

В C# 4.0 отражение не требуется, поскольку DLR может вызывать его, используя типы времени выполнения. Поскольку использование библиотеки DLR является своего рода проблемой динамически (вместо компилятора C#, генерирующего код для вас), фреймворк Динамитей с открытым исходным кодом (стандарт .net 1.5) дает вам простой кэшированный доступ во время выполнения к тем же вызовам, которые генерирует компилятор. для тебя.

var name = InvokeMemberName.Create;
Dynamic.InvokeMemberAction(this, name("GenericMethod", new[]{myType}));


var staticContext = InvokeContext.CreateStatic;
Dynamic.InvokeMemberAction(staticContext(typeof(Sample)), name("StaticMethod", new[]{myType}));

Вызов универсального метода с параметром типа, известным только во время выполнения, можно значительно упростить, используя тип dynamic вместо API отражения.

Чтобы использовать этот метод, тип должен быть известен по фактическому объекту (а не только по экземпляру класса Type). В противном случае вам придется создать объект этого типа или использовать стандартный API отражения решение. Вы можете создать объект, используя метод Activator.CreateInstance.

Если вы хотите вызвать универсальный метод, тип которого при "нормальном" использовании должен был бы быть определен, тогда дело просто доходит до преобразования объекта неизвестного типа в dynamic. Вот пример:

class Alpha { }
class Beta { }
class Service
{
    public void Process<T>(T item)
    {
        Console.WriteLine("item.GetType(): " + item.GetType()
                          + "\ttypeof(T): " + typeof(T));
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new Alpha();
        var b = new Beta();

        var service = new Service();
        service.Process(a); // Same as "service.Process<Alpha>(a)"
        service.Process(b); // Same as "service.Process<Beta>(b)"

        var objects = new object[] { a, b };
        foreach (var o in objects)
        {
            service.Process(o); // Same as "service.Process<object>(o)"
        }
        foreach (var o in objects)
        {
            dynamic dynObj = o;
            service.Process(dynObj); // Or write "service.Process((dynamic)o)"
        }
    }
}

И вот результат этой программы:

item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta
item.GetType(): Alpha    typeof(T): System.Object
item.GetType(): Beta     typeof(T): System.Object
item.GetType(): Alpha    typeof(T): Alpha
item.GetType(): Beta     typeof(T): Beta

Process - это универсальный метод экземпляра, который записывает реальный тип переданного аргумента (с помощью метода GetType()) и тип универсального параметра (с помощью оператора typeof).

Приведя аргумент объекта к типу dynamic, мы отложили предоставление параметра типа до времени выполнения. Когда метод Process вызывается с аргументом dynamic, компилятор не заботится о типе этого аргумента. Компилятор генерирует код, который во время выполнения проверяет реальные типы переданных аргументов (с помощью отражения) и выбирает лучший метод для вызова. Здесь есть только один универсальный метод, поэтому он вызывается с соответствующим параметром типа.

В этом примере результат такой же, как если бы вы написали:

foreach (var o in objects)
{
    MethodInfo method = typeof(Service).GetMethod("Process");
    MethodInfo generic = method.MakeGenericMethod(o.GetType());
    generic.Invoke(service, new object[] { o });
}

Версия с динамическим типом определенно короче и ее легче писать. Также не стоит беспокоиться о производительности многократного вызова этой функции. Следующий вызов с аргументами того же типа должен быть быстрее благодаря механизму кеширование в DLR. Конечно, вы можете написать код, который кеширует вызываемые делегаты, но, используя тип dynamic, вы получаете такое поведение бесплатно.

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

class Program
{
    static void Main(string[] args)
    {
        object obj = new Alpha();

        Helper((dynamic)obj);
    }

    public static void Helper<T>(T obj)
    {
        GenericMethod<T>();
    }

    public static void GenericMethod<T>()
    {
        Console.WriteLine("GenericMethod<" + typeof(T) + ">");
    }
}

Повышенная безопасность типа

Что действительно замечательно в использовании объекта dynamic в качестве замены для использования API отражения, так это то, что вы теряете только проверку времени компиляции этого конкретного типа, о котором вы не знаете до времени выполнения. Остальные аргументы и имя метода статически анализируются компилятором, как обычно. Если вы удалите или добавите дополнительные аргументы, измените их типы или переименуйте имя метода, вы получите ошибку времени компиляции. Этого не произойдет, если вы предоставите имя метода в виде строки в Type.GetMethod и аргументы в виде массива объектов в MethodInfo.Invoke.

Ниже приведен простой пример, показывающий, как некоторые ошибки могут быть обнаружены во время компиляции (закомментированный код), а другие - во время выполнения. Он также показывает, как DLR пытается решить, какой метод вызвать.

interface IItem { }
class FooItem : IItem { }
class BarItem : IItem { }
class Alpha { }

class Program
{
    static void Main(string[] args)
    {
        var objects = new object[] { new FooItem(), new BarItem(), new Alpha() };
        for (int i = 0; i < objects.Length; i++)
        {
            ProcessItem((dynamic)objects[i], "test" + i, i);

            //ProcesItm((dynamic)objects[i], "test" + i, i);
            //compiler error: The name 'ProcesItm' does not
            //exist in the current context

            //ProcessItem((dynamic)objects[i], "test" + i);
            //error: No overload for method 'ProcessItem' takes 2 arguments
        }
    }

    static string ProcessItem<T>(T item, string text, int number)
        where T : IItem
    {
        Console.WriteLine("Generic ProcessItem<{0}>, text {1}, number:{2}",
                          typeof(T), text, number);
        return "OK";
    }
    static void ProcessItem(BarItem item, string text, int number)
    {
        Console.WriteLine("ProcessItem with Bar, " + text + ", " + number);
    }
}

Здесь мы снова выполняем какой-то метод, приводя аргумент к типу dynamic. На время выполнения откладывается только проверка типа первого аргумента. Вы получите ошибку компилятора, если имя вызываемого метода не существует или если другие аргументы недействительны (неправильное количество аргументов или неправильные типы).

Когда вы передаете аргумент dynamic методу, это вызов недавно связанный. Разрешение перегрузки метода происходит во время выполнения и пытается выбрать лучшую перегрузку. Поэтому, если вы вызываете метод ProcessItem с объектом типа BarItem, вы фактически вызываете неуниверсальный метод, потому что он лучше подходит для этого типа. Однако вы получите ошибку времени выполнения, когда передадите аргумент типа Alpha, потому что нет метода, который мог бы обрабатывать этот объект (общий метод имеет ограничение where T : IItem, а класс Alpha не реализует этот интерфейс). Но в том-то и дело. У компилятора нет информации о том, что этот вызов действителен. Вы, как программист, знаете это и должны убедиться, что этот код работает без ошибок.

Тип возврата: ошибка

Когда вы вызываете непустой метод с параметром динамического типа, его возвращаемый тип, вероятно, будет быть dynamic тоже. Итак, если вы измените предыдущий пример на этот код:

var result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

то тип объекта результата будет dynamic. Это потому, что компилятор не всегда знает, какой метод будет вызван. Если вы знаете тип возвращаемого значения вызова функции, вам следует преобразовать его в требуемый тип, чтобы остальная часть кода была статически типизирована:

string result = ProcessItem((dynamic)testObjects[i], "test" + i, i);

Если тип не совпадает, вы получите сообщение об ошибке выполнения.

Фактически, если вы попытаетесь получить значение результата в предыдущем примере, вы получите ошибку времени выполнения на второй итерации цикла. Это потому, что вы пытались сохранить возвращаемое значение функции void.

Мариуш сбит с толку: «Однако вы получите ошибку времени выполнения, если передадите аргумент типа Alpha, потому что нет метода, который мог бы обрабатывать этот объект». Если я вызываю var a = new Alpha () ProcessItem (a, «test» + i , i) Почему бы универсальному методу ProcessItem не справиться с этим эффективно, выводя «Общий элемент процесса»?

Alex Edelstein 21.03.2015 21:51

@AlexEdelstein Я отредактировал свой ответ, чтобы немного уточнить. Это потому, что общий метод ProcessItem имеет общее ограничение и принимает только объект, реализующий интерфейс IItem. Когда вы вызовете ProcessItem(new Aplha(), "test" , 1); или ProcessItem((object)(new Aplha()), "test" , 1);, вы получите ошибку компилятора, но при преобразовании в dynamic вы откладываете эту проверку до времени выполнения.

Mariusz Pawelski 23.03.2015 14:38

Отличный ответ и объяснение, отлично работает для меня. Намного лучше, чем принятый ответ, короче, чтобы писать, эффективнее и безопаснее.

ygoe 28.08.2015 12:08

Добавление к Ответ Адриана Галлеро:

Вызов универсального метода из информации о типе включает три шага.

TL; DR: вызов известного универсального метода с типом объекта может быть выполнен следующим образом:

((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition()
    .MakeGenericMethod(typeof(string))
    .Invoke(this, null);

где GenericMethod<object> - это имя вызываемого метода и любой тип, удовлетворяющий общим ограничениям.

(Действие) соответствует сигнатуре вызываемого метода, например (Func<string,string,int> или Action<bool>)

Шаг 1 - получение MethodInfo для определения универсального метода

Метод 1. Используйте GetMethod () или GetMethods () с соответствующими типами или флагами привязки.

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");

Метод 2: создайте делегата, получите объект MethodInfo и затем вызовите GetGenericMethodDefinition

Изнутри класса, который содержит методы:

MethodInfo method = ((Action)GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

Извне класса, который содержит методы:

MethodInfo method = ((Action)(new Sample())
    .GenericMethod<object>)
    .Method
    .GetGenericMethodDefinition();

MethodInfo method = ((Action)Sample.StaticMethod<object>)
    .Method
    .GetGenericMethodDefinition();

В C# имя метода, то есть «ToString» или «GenericMethod», фактически относится к группе методов, которые могут содержать один или несколько методов. Пока вы не укажете типы параметров метода, неизвестно, какие метод, о котором вы говорите.

((Action)GenericMethod<object>) относится к делегату для определенного метода. ((Func<string, int>)GenericMethod<object>) относится к другой перегрузке GenericMethod

Метод 3: создайте лямбда-выражение, содержащее выражение вызова метода, получите объект MethodInfo, а затем GetGenericMethodDefinition

MethodInfo method = ((MethodCallExpression)((Expression<Action<Sample>>)(
    (Sample v) => v.GenericMethod<object>()
    )).Body).Method.GetGenericMethodDefinition();

Это разбивается на

Создайте лямбда-выражение, в котором тело является вызовом желаемого метода.

Expression<Action<Sample>> expr = (Sample v) => v.GenericMethod<object>();

Извлеките тело и приведите к MethodCallExpression

MethodCallExpression methodCallExpr = (MethodCallExpression)expr.Body;

Получить определение универсального метода из метода

MethodInfo methodA = methodCallExpr.Method.GetGenericMethodDefinition();

Шаг 2 вызывает MakeGenericMethod для создания универсального метода с соответствующим типом (ами).

MethodInfo generic = method.MakeGenericMethod(myType);

Шаг 3 вызывает метод с соответствующими аргументами.

generic.Invoke(this, null);

Это мои 2 цента на основе Ответ Гракса, но с двумя параметрами, необходимыми для универсального метода.

Предположим, ваш метод определен в классе Helpers следующим образом:

public class Helpers
{
    public static U ConvertCsvDataToCollection<U, T>(string csvData)
    where U : ObservableCollection<T>
    {
      //transform code here
    }
}

В моем случае тип U всегда является наблюдаемой коллекцией, хранящей объект типа T.

Поскольку у меня есть предопределенные типы, я сначала создаю «фиктивные» объекты, которые представляют наблюдаемую коллекцию (U) и объект, хранящийся в ней (T), и которые будут использоваться ниже для получения их типа при вызове Make

object myCollection = Activator.CreateInstance(collectionType);
object myoObject = Activator.CreateInstance(objectType);

Затем вызовите GetMethod, чтобы найти свою универсальную функцию:

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

Пока что вышеупомянутый вызов в значительной степени идентичен тому, что было объяснено выше, но с небольшой разницей, когда вам нужно передать ему несколько параметров.

Вам необходимо передать массив Type [] в функцию MakeGenericMethod, которая содержит типы «фиктивных» объектов, которые были созданы выше:

MethodInfo generic = method.MakeGenericMethod(
new Type[] {
   myCollection.GetType(),
   myObject.GetType()
});

Как только это будет сделано, вам нужно вызвать метод Invoke, как упоминалось выше.

generic.Invoke(null, new object[] { csvData });

И вы сделали. Работает шарм!

Обновлено:

Как подчеркнул @Bevan, мне не нужно создавать массив при вызове функции MakeGenericMethod, поскольку она принимает параметры, и мне не нужно создавать объект для получения типов, так как я могу просто передавать типы непосредственно в эту функцию. В моем случае, поскольку у меня есть типы, предопределенные в другом классе, я просто изменил свой код на:

object myCollection = null;

MethodInfo method = typeof(Helpers).
GetMethod("ConvertCsvDataToCollection");

MethodInfo generic = method.MakeGenericMethod(
   myClassInfo.CollectionType,
   myClassInfo.ObjectType
);

myCollection = generic.Invoke(null, new object[] { csvData });

myClassInfo содержит 2 свойства типа Type, которые я устанавливаю во время выполнения на основе значения перечисления, переданного конструктору, и предоставит мне соответствующие типы, которые я затем использую в MakeGenericMethod.

Еще раз спасибо за выделение этого @Bevan.

Аргументы MakeGenericMethod() содержат ключевое слово параметры, поэтому вам не нужно создавать массив; вам также не нужно создавать экземпляры для получения типов - methodInfo.MakeGenericMethod(typeof(TCollection), typeof(TObject)) будет достаточно.

Bevan 27.10.2015 00:58

Никто не предоставил решение "классическое отражение", поэтому вот полный пример кода:

using System;
using System.Collections;
using System.Collections.Generic;

namespace DictionaryRuntime
{
    public class DynamicDictionaryFactory
    {
        /// <summary>
        /// Factory to create dynamically a generic Dictionary.
        /// </summary>
        public IDictionary CreateDynamicGenericInstance(Type keyType, Type valueType)
        {
            //Creating the Dictionary.
            Type typeDict = typeof(Dictionary<,>);

            //Creating KeyValue Type for Dictionary.
            Type[] typeArgs = { keyType, valueType };

            //Passing the Type and create Dictionary Type.
            Type genericType = typeDict.MakeGenericType(typeArgs);

            //Creating Instance for Dictionary<K,T>.
            IDictionary d = Activator.CreateInstance(genericType) as IDictionary;

            return d;

        }
    }
}

Вышеупомянутый класс DynamicDictionaryFactory имеет метод

CreateDynamicGenericInstance(Type keyType, Type valueType)

и он создает и возвращает экземпляр IDictionary, типы ключей и значений которого точно указаны в вызове keyType и valueType.

Вот полный пример, как вызвать этот метод для создания и использования Dictionary<String, int>:

using System;
using System.Collections.Generic;

namespace DynamicDictionary
{
    class Test
    {
        static void Main(string[] args)
        {
            var factory = new DictionaryRuntime.DynamicDictionaryFactory();
            var dict = factory.CreateDynamicGenericInstance(typeof(String), typeof(int));

            var typedDict = dict as Dictionary<String, int>;

            if (typedDict != null)
            {
                Console.WriteLine("Dictionary<String, int>");

                typedDict.Add("One", 1);
                typedDict.Add("Two", 2);
                typedDict.Add("Three", 3);

                foreach(var kvp in typedDict)
                {
                    Console.WriteLine("\"" + kvp.Key + "\": " + kvp.Value);
                }
            }
            else
                Console.WriteLine("null");
        }
    }
}

Когда вышеуказанное консольное приложение выполняется, мы получаем правильный ожидаемый результат:

Dictionary<String, int>
"One": 1
"Two": 2
"Three": 3

Вдохновленный Ответ загадочности - предположим, у вас есть два (или более) класса, например

public class Bar { }
public class Square { }

и вы хотите вызвать метод Foo<T> с Bar и Square, который объявлен как

public class myClass
{
    public void Foo<T>(T item)
    {
        Console.WriteLine(typeof(T).Name);
    }
}

Затем вы можете реализовать Способ расширения, например:

public static class Extension
{
    public static void InvokeFoo<T>(this T t)
    {
        var fooMethod = typeof(myClass).GetMethod("Foo");
        var tType = typeof(T);
        var fooTMethod = fooMethod.MakeGenericMethod(new[] { tType });
        fooTMethod.Invoke(new myClass(), new object[] { t });
    }
}

При этом вы можете просто вызвать Foo, например:

var objSquare = new Square();
objSquare.InvokeFoo();

var objBar = new Bar();
objBar.InvokeFoo();

который работает для каждого класса. В этом случае он выведет:

Square
Bar

Другие вопросы по теме