2015-11-04 6 views
1

Рассмотрим следующий код:Переходя параметр типа для универсального типа без каких-либо ограничений бросает исключение ограничений

IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof(object)) 
             .GetTypes() 
             .Where(t => t.IsGenericType) 
             .Where(t => t.GetGenericArguments().Length == 1) 
             .Where(t => t.GetGenericArguments().Single().GetGenericParameterConstraints().Length == 0); 

oneParameterTypes должен держать все общие типы в System.dll сборки, которые могут быть переданы единый параметр универсального типа и не применяйте к нему ограничений.

Теперь давайте перейдем в качестве параметра типа:

IEnumerable<Type> intParameterTypes = oneParameterTypes.Select(t => t.MakeGenericType(typeof(int))) 
                 .ToList(); 

Это должно работать, не так ли? Я имею в виду, что все типы в oneParameterTypes не должны иметь ограничений типа, поэтому System.Int32 должен быть допустимым типом.

Тем не менее, линия выдает следующее исключение:

GenericArguments [0], 'System.Int32', на 'System.RuntimeType + ListBuilder`1 [Т]' нарушает ограничение типа «Т ».

Что это ListBuilder'1 тип и почему он в oneParameterTypes, если он имеет ограничение типа? Почему мой фильтр Where не работает?

+0

Я думаю, это потому, что класс имеет ограничение на общем типе для 'class', но не для определенного класса. (а не 'struct') Например, если вы запустите свой код в пустом' public class Test , где T: class {} ', он будет работать точно так же. С головы до ног я не уверен, как именно вы это проверяете, или почему это обязательно не распространяется на ваши предложения 'Where'. –

+0

@ChrisSinclair Это неутешительно. 'где T: class' является ограничением типа, но он не включен в' type.GetGenericParameterConstraints() '. Это намечено или это ошибка? –

+0

Я так не думаю.См. Ответ [@Szabolcs] (http://stackoverflow.com/a/33530532/1269654). Это просто дополнительные атрибуты, которые вам нужно искать. Я считаю, что 'GetGenericParameterConstraints' проверяет только ограничения наследования _type, а не ограничения' class/struct' или ковариации/контравариантности. EDIT: Возможно, они считаются «атрибутами», а не ограничениями. Несмотря на это, я согласен с тем, что именование немного неоднозначно или сбивает с толку, даже если оно технически правильно (предполагается, что оно). –

ответ

2

Я думаю, что Крис прав.

Попробуйте так:

IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof (object)) 
    .GetTypes() 
    .Where(t => t.IsGenericType) 
    .Where(t => t.GetGenericArguments().Length == 1) 
    .Where(t => t.GetGenericArguments().Single().GetGenericParameterConstraints().Length == 0 && 
       !genericArgument.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint); 
    }); 
+1

Я думаю, что это, вероятно, сработает, хотя вместо этого вы можете проверить с помощью '! = GenericParameterAttributes.ReferenceTypeConstraint' (так как есть несколько других, которые вы можете включить, например' in' и 'out'). –

+0

Правильно. Моя первоначальная попытка вернула 125 элементов, то есть с символом '! = GenericParameterAttributes.ReferenceTypeConstraint' 141. –

+1

Кроме того, я по-прежнему ошибочен. Я не заметил, что «GenericParameterAttributes» - это побитовое перечисление флагов, что в ретроспективе имеет смысл. Поэтому вы не должны проверять значение '! = GenericParameterAttributes.ReferenceTypeConstraint', но вместо этого проверяйте побитовые флажки, чтобы увидеть, содержит ли он этот флаг. –

0

TYPEOF() или метод .GetType() не возвращает фактический класс, вместо этого они будут возвращать представление типа объекта, который поможет нам в использовании имени типа как строка.

Вместо этого, вы можете использовать классы отражения и найти класс этого типа и использовать его в качестве параметра

1

документация не совсем понятно, о том, что на самом деле Type.GetGenericParameterConstraints ищет:

Используйте IsClass, чтобы определить, является ли ограничение ограничением базового класса; если свойство возвращает false, ограничение является ограничением интерфейса. Если параметр типа не имеет ограничений класса и ограничений интерфейса, возвращается пустой массив.

Вы можете косвенно понять, что он будет проверять только для базового класса или интерфейса ограничения. Чтобы получить полную картину, вокруг которой существуют ограничения, вам потребуется дополнительная проверка на GenericParameterAttributes. Вы можете использовать HasFlag или побитовая маску:

IEnumerable<Type> oneParameterTypes = Assembly.GetAssembly(typeof(object)) 
       .GetTypes() 
       .Where(t => t.IsGenericType) 
       .Where(t => t.GetGenericArguments().Length == 1) 
       .Where(t => t.GetGenericArguments().Single().GetGenericParameterConstraints().Length == 0 && !t.GenericParameterAttributes.HasFlag(GenericParameterAttributes.ReferenceTypeConstraint); 
+0

Правильно, кроме одного маленького nitpicking, это называется HasFlag, а не HasFlags. –

+0

@Szabolcs Thx, исправлено. –

Смежные вопросы