2009-08-28 60 views
17

Recibo este error en una rutina que utiliza la reflexión para volcar algunas propiedades de objeto, algo así como el código siguiente.El método solo se puede invocar en un tipo para el que Type.IsGenericParameter es verdadero

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); 

     ... 
    } 
} 

el error real es "se inició una excepción por el destino de una invocación" con una excepción interior del "método puede ser llamado sólo en un tipo para el que Type.IsGenericParameter es verdadero".

En esta etapa en el obj depurador aparece como

{Name = "SqlConnection" FullName = "System.Data.SqlClient.SqlConnection"} 

con el System.RuntimeType tipo

El método m es {System.Reflection.MethodBase DeclaringMethod}

Nota que obj es del tipo System.RuntimeType y sus miembros contiene 188 elementos, mientras que un tipo simple de (System.Data.SqlClient.SqlConnection) .GetMembers (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance) solo devuelve 65.

Intenté comprobar isGenericParameter tanto en obj como en p.PropertyType, pero esto parece ser falso para la mayoría de las propiedades, incluidas aquellas donde funciona p.GetValue.

Entonces, ¿qué es exactamente un "Tipo para el que Type.IsGenericParameter es verdadero" y más importante aún ¿cómo puedo evitar este error sin intentar/atrapar?

Respuesta

13

en primer lugar, usted ha hecho en supuesto incorrecto, es decir, ha supuesto que members ha devuelto los miembros de una instancia de System.Data.SqlClient.SqlConnection, que no tiene. Lo que se ha devuelto son los miembros de una instancia de System.Type.

A partir de la documentación de MSDN para DeclaringType:

Cómo recuperar la propiedad DeclaringMethod en un tipo cuya IsGenericParameter propiedad es falsa emite una InvalidOperationException.

Entonces ... es comprensible que se haya lanzado un InvalidOperationException, ya que, naturalmente, no se trata de un tipo genérico abierto aquí. Consulte Marc Gravells answer para obtener una explicación de los tipos genéricos abiertos.

+2

Creo que estoy empezando a ver la luz . No está diciendo que p.GetValue puede 'solo invocar un tipo para el que Type.IsGenericParameter es verdadero', sino que la propiedad subyacente representada por p, que en este caso es DeclaringMethod, solo se puede llamar es Type.IsGenericParameter es cierto. – sgmoore

+1

Exactamente: eso es lo que "Excepción ha sido lanzada por el objetivo de una invocación" significa, el "objetivo de una invocación" en este caso es el getter de propiedad 'DeclaringMethod', y usted obtendría la misma excepción' IsGenericParameter' al leer 'obj.DeclaringMethod' directamente. – stevemegson

+0

He marcado esta respuesta como mi respuesta aceptada, ya que fue la más útil, pero en realidad la mayoría de las otras respuestas me ayudaron también. Así que gracias a todos. – sgmoore

13

Entonces, ¿qué es un "tipo para el que Type.IsGenericParameter es cierto"

Eso significa que es un argumento de tipo genérico en un tipo genérico abierto - es decir, donde no hemos elegido un T todavía; por ejemplo:

// true 
bool isGenParam = typeof(List<>).GetGenericArguments()[0].IsGenericParameter; 

// false (T is System.Int32) 
bool isGenParam = typeof(List<int>).GetGenericArguments()[0].IsGenericParameter; 

So; ¿Tienes algunos genéricos abiertos dando vueltas? Tal vez si puede dar un ejemplo de dónde obtuvo su obj?

+0

El OP indica el tipo de en obj su System.RuntimeType – AnthonyWJones

+0

Por lo que puedo ver, el problema no es tanto la existencia de genéricos abiertos, sino la existencia de tipos normales :) –

+0

Comienzo con una excepción (en este caso, una SqlException) que se pasa a mi rutina de volcado. La rutina de volcado es recursiva, por lo que se invoca el volcado en todas las propiedades y campos de la excepción. Hay algunos genéricos en mi código, pero no hay genéricos abiertos. Lo que me confunde es que el error parece decir que p.GetValue solo funciona si GenericParameter es verdadero, lo que simplemente no es cierto. – sgmoore

3

Todas las pistas están ahí. El tipo de obj es la clase Type en sí misma (o más bien el extraño derivado de RuntimeType).

En el punto de falla, el bucle ha llegado a la propiedad de clase Type llamada DeclaringMethod. Sin embargo, el tipo que esta instancia de la clase Type está describiendo es System.Data.SqlClient.SqlConnection que no es un tipo genérico de un método.

Por lo tanto, intentar invocar los resultados de DeclaringMethod en la excepción.

La clave es que está examinando el tipo de la clase Type. Es un poco circular, pero piense en esto: -

SqlConnection s = new SqlConnection(); 
Type t = s.GetType() 
Type ouch = t.GetType() 

¿Qué está describiendo la clase ouch?

1

¿Cómo puedo evitar este error sin intentar/atrapar?

Es casi seguro que no.Cuando llamas al p.GetValue, llamas al comprador en esa propiedad, lo que podría arrojar cualquier tipo de excepción. Por ejemplo, SqlConnection.ServerVersion lanzará una excepción si la conexión está cerrada, y usted tiene que manejar eso.

¿De dónde vienen estos miembros adicionales?

Su obj ya contiene el objeto que representa RuntimeTypeSqlConnection, en lugar de una instancia de SqlConnection. obj.GetMembers() devolvería los 65 miembros de la clase SqlConnection, pero al volver a llamar al GetType(), obtendrá los 188 miembros de RuntimeType.

¿Qué es IsGenericParameter?

En lugar de representar una clase, puede tener una instancia de RuntimeType que representa un parámetro genérico para una clase o método (El T y TOutput en List<T>.ConvertAll<TOutput>. En este caso, DeclaringMethod en el objeto que representa TOutput le permitirá obtener un objeto MethodInfo que representa el método ConvertAll<>. sin embargo, cuando el RuntimeType representa una clase, la idea de un método que se declara que no tiene sentido. es por eso que la lectura de la propiedad hace que la excepción que viste.

Cuestiones relacionadas