Вызовите универсальный html-помощник динамически с учетом модели [duplicate]

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

nav .dropdown-menu {
    display: block;
    overflow: hidden;
    max-height: 0;
    opacity: 0;
    transition: max-height 500ms, opacity 300ms;
    -webkit-transition: max-height 500ms, opacity 300ms;
}
nav .dropdown:hover .dropdown-menu {
    max-height: 500px;
    opacity: 1;
    transition: max-height 0, opacity 300ms;
    -webkit-transition: max-height 0, opacity 300ms;
}

Вы также можете использовать height вместо max-height, если вы укажете оба значения, так как height:auto не разрешено с transition s. Значение зависания max-height должно быть больше, чем height меню может быть.

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

5 ответов

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

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

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

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

928
ответ дан Jon Skeet 25 August 2018 в 05:41
поделиться

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

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 25 August 2018 в 05:41
поделиться

Это мои 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.

107
ответ дан Community 25 August 2018 в 05:41
поделиться

Никто не предоставил « классическое решение 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 25 August 2018 в 05:41
поделиться

С 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 25 August 2018 в 05:41
поделиться
Другие вопросы по тегам:

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