Как создать общий объект с помощью Reflection

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

Я пытался использовать отражение непосредственно для типа, используя System.Reflection.FieldInfo.GetValue, но это не работает для классов с универсальными типами. Поэтому я думаю, что мне нужно создать экземпляр класса, чтобы увидеть значение.

«Типы», которые у меня есть, я извлек, прочитав библиотеки DLL в корзине и используя Reflection, чтобы найти типы, которые наследуются от моего интерфейса.

Я использую .NET 4.5

вот документация, которая, кажется, точно объясняет, что мне нужно сделать http://msdn.microsoft.com/en-us/library/b8ytshk6.aspx

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

foreach (var item in types)
{
    var ts = item.GetField("DefaultTimeToExpire");
    Type[] typeArguments = item.GetGenericArguments();
    if (ts != null)
    {
        var t = item.MakeGenericType(typeArguments);
        var obj = Activator.CreateInstance(t);
        var timespan = obj.DefaultTimeToExpire;
        subscriberInfos.Add(new Tuple<string, Type, TimeSpan>(item.Name, item, timespan));
    }
}

Я звоню в GetField для поиска элементов с полем «DefaultTimeToExpire», пока эта часть работает хорошо, чтобы найти нужный мне тип. Затем я вызываю GetGenericArguments, который возвращает ожидаемый массив типа Arguments. затем я вызываю MakeGenericType и, наконец, создаю экземпляр, который дает мне сообщение об ошибке

«Невозможно создать экземпляр BusinessLogic.TestSubscriberXXX`1[Message], поскольку Type.ContainsGenericParameters имеет значение true».

Похоже, это именно то, что я должен делать. Спасибо


person Noel    schedule 28.08.2012    source источник
comment
Возможно ли, что в вашем typeArguments недостаточно типов, чтобы установить все параметры универсального типа?   -  person Dan Puzey    schedule 29.08.2012
comment
Странная ошибка, трудно догадаться, как вы оказались с открытым типом. Ярлык состоит в том, чтобы просто использовать Activator.CreateInstance(item.GetType()).   -  person Hans Passant    schedule 29.08.2012


Ответы (1)


Чтобы создать экземпляр универсального типа, вам необходимо знать фактические значения (типы), которые должны быть заменены параметрами его типа. Метод GetGenericArguments(), являющийся формой отражения, дает вам только тип аргументов, а не их фактические значения. Значения зависят от вас... в этом весь смысл дженериков.

Если item является типом, подобным List<T>, то item.GetGenericArguments() вернет массив, содержащий поддельный «тип», представляющий параметр типа T (с его свойством IsGenericParameter, установленным в true). Следовательно, передача этого типа параметра обратно в item.MakeGenericType() просто создаст другой открытый общий тип, эквивалентный исходному. Чтобы закрыть универсальный тип, чтобы его можно было создать, вам необходимо указать фактический (непараметрический) аргумент типа, например int.

Например, typeof(List<>).MakeGenericType(typeof(int)) вернет typeof(List<int>), а typeof(List<>).MakeGenericType(typeof(List<>).GetGenericArguments()) снова просто вернет typeof(List<>). Это то, что происходит в вашем коде.

Извините, если это немного непрозрачно, я не знаю, как еще это объяснить. Суть в том, что тип, подобный List<T>, полезен только в том случае, если у вас есть тип, который вы хотите заменить вместо T.

person luksan    schedule 29.08.2012
comment
Item — это тип, подобный List‹User›, когда я вызываю GetGenericArguments() и проверяю, что возвращается, он возвращает тип, который я ожидаю. Пользователь. Одна вещь, которая вызывает подозрение, это то, что FullName имеет значение null, хотя у него есть полное имя сборки. - person Noel; 29.08.2012
comment
FullName, являющийся нулевым, странен... Я никогда раньше этого не видел. Если он возвращает пользователя (а пользователь является фактическим типом, а не параметром типа), то у вас уже есть закрытый универсальный тип. После этого вы сможете просто выполнить Activator.CreateInstance(item)'. - person luksan; 30.08.2012
comment
Хорошо, я понятия не имею, почему я не могу создать правильный общий тип, чтобы позволить CreateInstance работать, но я решил свою проблему. Этот код на самом деле находится внутри универсального класса, который реализует тип, который я пытаюсь создать в качестве своего универсального типа. Поэтому я могу просто написать: ` var typeArg = typeof(T); var t = item.MakeGenericType(typeArg); ISubscriber‹T› subscriber = (ISubscriber‹T›)Activator.CreateInstance(t); var timespan = subscriber.DefaultTimeToExpire; subscriberInfos.Add(новый кортеж‹string, Type, TimeSpan›(item.Name, item, timespan)); ` - person Noel; 30.08.2012