.NET Generics: использование типа, созданного активатором, в качестве универсального показывает неправильный тип? Требуется обходной путь

Это действительно поставило меня в тупик сегодня. Я уверен, что это просто, но... Вот мой пример кода:

using System;
using System.Collections;

namespace ConsoleApplication1
{
    class Program
    {
        public ArrayList SomeProp { get; set; }

        static void Main(string[] args)
        {
            // Get the Type of a property by reflection.
            Type myPropType = typeof(Program).GetProperty("SomeProp").PropertyType;

            // Create an instance of the determined Type.
            var x = Activator.CreateInstance(myPropType);

            // Now try to use that instance, passing to a method that takes a generic.
            WhatAmI(x);

            Console.ReadKey();
        }

        private static void WhatAmI<T>(T x)
        {
            Console.WriteLine("T is: " + typeof(T).FullName);
            Console.WriteLine("x is: " + x.GetType().FullName);
        }
    }
}

Вывод здесь:

T is: System.Object
x is: System.Collections.ArrayList

В основном, что здесь происходит, у меня есть некоторая собственность в каком-то классе. Неважно, что/откуда это. Важной частью является то, что я получаю PropertyInfo для этого свойства. Оттуда я создаю новый экземпляр его типа с активатором.

Теперь, если я передам этот созданный экземпляр функции, которая принимает универсальный параметр, общий тип всегда будет восприниматься как объект, потому что Activator.CreateInstance() возвращает объект, поэтому «var x» является объектом, а не в этом случае, список массивов.

Мне нужно, чтобы общий тип был фактическим типом, в его случае «ArrayList».

Я знаю, что есть Activator.CreateInstance(), который я могу использовать вместо этого, но я не могу использовать тип (PropertyInfo.PropertyType) в угловых скобках для этого метода.

Я также мог бы просто привести возвращаемый объект, например:

myPropType x = (myPropType)Activator.CreateInstance(myPropType);

Но очевидно, что это не компилируется... К тому же, это все равно не будет действительным, потому что приведение - это время компиляции, и я не знаю тип до времени выполнения, но концептуально это то, что мне нужно...

Итак, я застрял с этим типом, но я не могу понять, как передать его методу WhatAmI() и сделать так, чтобы T был ArrayList, а не Object.

Идеи?


person CodingWithSpike    schedule 07.01.2009    source источник


Ответы (1)


Чтобы вызвать общие методы с использованием Type во время выполнения, вам нужно использовать отражение - и, в частности, MakeGenericMethod, что-то вроде:

typeof(Program).GetMethod("WhatAmI",
    BindingFlags.Static | BindingFlags.NonPublic)
    .MakeGenericMethod(x.GetType()).Invoke(null, new object[] { x });

В противном случае компилятор выводит <T> из типа переменной - в данном случае object.

Одним из побочных моментов здесь является то, что вы можете улучшить ситуацию, выполнив отражение один раз, то есть не используйте Activator, а вместо этого используйте общее ограничение:

    private static void WhatAmI<T>() where T : new()
    {
        T x = new T();
        Console.WriteLine("T is: " + typeof(T).FullName);
        Console.WriteLine("x is: " + x.GetType().FullName);
    }

Определяет метод без аргумента, но с конструктором по умолчанию; тогда:

    // Get the Type of a property by reflection.
    Type myPropType = typeof(Program).GetProperty("SomeProp").PropertyType;

    // Now call a generic method just using the type
    typeof(Program).GetMethod("WhatAmI",
        BindingFlags.Static | BindingFlags.NonPublic)
            .MakeGenericMethod(myPropType).Invoke(null, null);

вызывает метод, просто используя тип — экземпляр создается внутри метода. Обычно это быстрее, чем повторное отражение.

person Marc Gravell    schedule 07.01.2009
comment
К счастью, в том месте, где мне нужно это применить, это мой собственный метод, который, как я знаю, является универсальным, поэтому он должен работать. Я мог видеть, что это становится реальной проблемой для разделяемых библиотек, где вы можете непреднамеренно вызвать метод, который, как вы не знаете, является универсальным. В любом случае, я попробую. Спасибо! - person CodingWithSpike; 07.01.2009
comment
Я никогда раньше не видел такого, где T : new(). Очень круто! Спасибо! - person CodingWithSpike; 07.01.2009