Net Core dbContext установлен динамически по строке [дубликат]

Причина, по которой вы должны использовать std :: vector, на самом деле потому, что вы хотите сохранить коллекцию int, а не указатель int, поэтому почему бы не использовать ее?

Это также решило бы ваш const ' ness issue.

Вот как это работает: объявление метода const приведет к тому, что все члены класса будут const. В вашем случае это означает, что в области метода ваш член data станет постоянным указателем на int. Это означает, что вы можете изменить значение int (также означая членов массива), пока data указывает на то же место. Используя std::vector, данные станут константой std::vector, на которую вы могли бы вызывать только функции const. Поэтому да, вы должны использовать std::vector.

850
задан Wai Ha Lee 28 November 2015 в 11:41
поделиться

6 ответов

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

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

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

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

926
ответ дан Jon Skeet 16 August 2018 в 03:30
поделиться
  • 1
    +1; обратите внимание, что GetMethod() по умолчанию использует только методы общего экземпляра, поэтому вам могут понадобиться BindingFlags.Static и / или BindingFlags.NonPublic. – user 17 December 2011 в 00:32
  • 2
    Правильная комбинация флагов - BindingFlags.NonPublic | BindingFlags.Instance (и необязательно BindingFlags.Static). – Lars Kemmann 15 February 2013 в 21:10
  • 3
    Вопрос о том, как сделать это с помощью статических методов, задает вопрос о том, как это сделать, и технически так возникает вопрос. generic.Invoke () первый параметр должен быть пустым при вызове статических методов. Первый параметр необходим только при вызове методов экземпляра. – Chris Moschini 22 March 2013 в 23:30
  • 4
    @ChrisMoschini: Добавил это к ответу. – Jon Skeet 22 March 2013 в 23:32
  • 5
    @gzou: Я добавил что-то в ответ - но обратите внимание, что для вызова общих методов в вопросе , dynamic не помогает, потому что вывод типа недоступен. (Нет аргументов, которые компилятор может использовать для определения аргумента типа.) – Jon Skeet 24 April 2015 в 09:44

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

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. Я не знаю, есть ли лучший способ.

136
ответ дан Ajay 16 August 2018 в 03:30
поделиться
  • 1
    В случаях, когда отражение используется для вызова метода, обычно считается, что имя метода открыто другим способом. Знание имени метода заранее не является обычным явлением. – Bevan 27 February 2011 в 22:59
  • 2
    Ну, я согласен на общее использование рефлексии. Но исходный вопрос заключался в том, как назвать «GenericMethod & lt; myType & gt; ()» Если бы этот синтаксис был разрешен, нам вообще не нужен GetMethod (). Но для вопроса «как писать» «GenericMethod & lt; myType & gt;»? Я думаю, что ответ должен включать способ избежать потери связи времени компиляции с GenericMethod. Теперь, если этот вопрос распространен или нет, я не знаю, но я знаю, что вчера у меня была эта точная проблема, и именно поэтому я приземлился в этом вопросе. – Adrian Gallero 28 February 2011 в 22:24
  • 3
    Вы можете сделать GenMethod.Method.GetGenericMethodDefinition() вместо this.GetType().GetMethod(GenMethod.Method.Name). Это немного чище и, вероятно, безопаснее. – Daniel Cassidy 10 May 2011 в 11:10
  • 4
    Что означает «myType»? в вашем образце? – Academy of Programmer 7 December 2011 в 15:45
  • 5
    Теперь вы можете использовать nameof(GenericMethod) – dmigo 17 March 2016 в 11:02

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

Чтобы использовать этот метод тип должен быть известен из фактического объекта (а не только для экземпляра класса Type). В противном случае вам нужно создать объект такого типа или использовать стандартное API 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).

Путем отбрасывания аргумента object в тип 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, который не реализует этот интерфейс ). Но в этом весь смысл. У компилятора нет информации о том, что этот вызов действителен. Вы, как программист, знаете это, и вы должны убедиться, что этот код работает без ошибок.

Тип возврата gotcha

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

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

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

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

Вы будете получить ошибку выполнения, если тип не соответствует.

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

107
ответ дан Community 16 August 2018 в 03:30
поделиться
  • 1
    Mariusz, confused by & quot; Однако вы получите ошибку времени выполнения, когда передаете аргумент типа Alpha, потому что нет метода, который может обрабатывать этот объект. & Quot; Если я вызову var a = new Alpha () ProcessItem (a, "test & quot; + i, i), почему бы не использовать общий метод ProcessItem эффективно, выводя« Общий элемент процесса »? – Alex Edelstein 21 March 2015 в 19:51
  • 2
    @AlexEdelstein Я отредактировал свой ответ, чтобы немного уточнить. Это связано с тем, что общий метод ProcessItem имеет общее ограничение и принимает только объект, реализующий интерфейс IItem. Когда вы назовете ProcessItem(new Aplha(), "test" , 1); или ProcessItem((object)(new Aplha()), "test" , 1);, вы получите ошибку компилятора, но при нажатии на dynamic вы отложите эту проверку во время выполнения. – Mariusz Pawelski 23 March 2015 в 12:38
  • 3
    Отличный ответ и объяснение, отлично работает для меня. Гораздо лучше, чем принятый ответ, короче писать, более результативно и безопаснее. – ygoe 28 August 2015 в 09:08

С C # 4.0 отражение не требуется, поскольку DLR может вызывать его с использованием типов времени выполнения. Так как использование библиотеки DLR представляет собой боль динамически (вместо кода генерации компилятора C # для вас), open source framework Dynamitey (.net standard 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}));
11
ответ дан jbtule 16 August 2018 в 03:30
поделиться

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

Предположим, что ваш метод определяется следующим образом в классе 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 });

И все готово.

UPDATE:

Поскольку @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.

6
ответ дан Community 16 August 2018 в 03:30
поделиться
  • 1
    Аргументы MakeGenericMethod() имеют ключевое слово params , поэтому вам не нужно создавать массив; вам также не нужно создавать экземпляры для получения типов - methodInfo.MakeGenericMethod(typeof(TCollection), typeof(TObject)). – Bevan 26 October 2015 в 22:58

Никто не предоставил « классическое решение Reflection », так что вот полный пример кода:

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
2
ответ дан Dimitre Novatchev 16 August 2018 в 03:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: