Метод может быть вызван только для типа, для которого Type.IsGenericParameter является истинным
Я получаю эту ошибку в подпрограмме, которая использует отражение, чтобы сбросить некоторые свойства объекта, что-то вроде кода ниже.
MemberInfo[] members = obj.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance) ;
foreach (MemberInfo m in members)
{
PropertyInfo p = m as PropertyInfo;
if (p != null)
{
object po = p.GetValue(obj, null);
...
}
}
Фактическая ошибка: "Исключение выбрано целью вызова"
с внутренним исключением "Метод может быть вызван только для типа, для которого Type.IsGenericParameter является истинным".
На этом этапе в отладчике obj появляется как
{Name = "SqlConnection" FullName = "System.Data.SqlClient.SqlConnection"}
с типом System.RuntimeType
Метод m является {System.Reflection.MethodBase DeclaringMethod}
Обратите внимание, что obj имеет тип System.RuntimeType и члены содержат 188 элементов, тогда как простой
typeof (System.Data.SqlClient.SqlConnection).GetMembers(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance) возвращает 65.
Я пробовал проверять isGenericParameter как на obj, так и на p.PropertyType, но это кажется ложным для большинства свойств, в том числе тех, где p.GetValue работает.
Так что же такое "Тип, для которого Type.IsGenericParameter является истинным" и что более важно
как избежать этой ошибки без try/catch?
Ответы
Ответ 1
Во-первых, вы сделали неверное предположение, то есть предположили, что members
возвратил члены экземпляра System.Data.SqlClient.SqlConnection
, которого у него нет. То, что было возвращено, это члены экземпляра System.Type
.
Из документации MSDN для DeclaringType:
Получение свойства DeclaringMethod
на типе IsGenericParameter
свойство ложно бросает InvalidOperationException
.
Итак... понятно, что создается InvalidOperationException
, так как, естественно, вы не имеете здесь никакого открытого типа. См. ответ Марка Гравеллса для объяснения открытых родовых типов.
Ответ 2
Так что же такое "Тип, для которого Type.IsGenericParameter истинно"
Это означает, что это общий аргумент типа в открытом родовом типе - т.е. где мы еще не выбрали T
; например:
// true
bool isGenParam = typeof(List<>).GetGenericArguments()[0].IsGenericParameter;
// false (T is System.Int32)
bool isGenParam = typeof(List<int>).GetGenericArguments()[0].IsGenericParameter;
Итак, есть ли у вас открытые дженерики? Возможно, если вы можете привести пример того, откуда вы получили obj
от?
Ответ 3
Все подсказки там. Тип obj - это сам класс Type
(или, скорее, странная производная RuntimeType).
В момент сбоя вы получаете свойство Type
класса DeclaringMethod
. Однако тип, описываемый этим экземпляром класса Type
, это System.Data.SqlClient.SqlConnection
, который не является общим типом метода.
Следовательно, попытка вызвать get на DeclaringMethod приводит к исключению.
Ключ: вы изучаете тип класса Type
. Его немного круговое, но подумайте об этом: -
SqlConnection s = new SqlConnection();
Type t = s.GetType()
Type ouch = t.GetType()
Что такое класс, описывающий?
Ответ 4
Как избежать этой ошибки без try/catch?
Ты почти наверняка не можешь. Когда вы вызываете p.GetValue
, вы вызываете getter на это свойство, которое может вызывать любое исключение. Например, SqlConnection.ServerVersion выдает исключение, если соединение закрыто, и вы должны это обработать.
Откуда берутся эти дополнительные члены?
Ваш obj
уже содержит объект RuntimeType
, представляющий SqlConnection
, а не экземпляр SqlConnection
. obj.GetMembers()
вернет 65 членов класса SqlConnection
, но снова вызов GetType()
, вы получите 188 членов RuntimeType
.
Что такое IsGenericParameter
?
Вместо представления класса вы можете иметь экземпляр RuntimeType
, который представляет общий параметр для класса или метода (T
и TOutput
в List<T>.ConvertAll<TOutput>
. В этом случае DeclaringMethod
on объект, представляющий TOutput
, позволит вам получить объект MethodInfo
, представляющий метод ConvertAll<>
. Однако, когда RuntimeType
представляет класс, идея метода объявления не имеет смысла. Именно поэтому чтение свойства вызывает исключение, которое вы видели.