2009-08-28 4 views
17

Viene visualizzato questo errore in una routine che utilizza la riflessione per scaricare alcune proprietà dell'oggetto, ad esempio il codice seguente.Il metodo può essere chiamato solo su un tipo per cui Type.IsGenericParameter è true

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

     ... 
    } 
} 

L'errore effettivo è "eccezione è stata generata dalla destinazione di una chiamata" con un'eccezione interna "Metodo può essere chiamato solo su un tipo per il quale Type.IsGenericParameter è vero."

In questa fase nel obj debugger appare come

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

al tipo System.RuntimeType

Il metodo m è {System.Reflection.MethodBase DeclaringMethod}

noti che obj è di tipo System.RuntimeType e membri contiene 188 elementi mentre un semplice tipo (System.Data.SqlClient.SqlConnection) .GetMembers (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance) restituisce solo 65.

Ho provato a verificare isGenericParameter su obj e p.PropertyType, ma questo sembra essere falso per la maggior parte delle proprietà, incluse quelle in cui p.GetValue funziona.

Quindi, qual è esattamente un "Tipo per cui Type.IsGenericParameter è true" e, ancora più importante, come evitare questo errore senza try/catch?

risposta

13

in primo luogo, che hai fatto in ipotesi errata, ovvero, hai assunto che members ha restituito i membri di un'istanza di System.Data.SqlClient.SqlConnection, che non ha. Ciò che è stato restituito sono i membri di un'istanza di System.Type.

Dalla documentazione MSDN per DeclaringType:

Ottenere il DeclaringMethod proprietà su un tipo il cui IsGenericParameter proprietà è falso getta un InvalidOperationException.

Quindi ... è comprensibile che venga lanciato uno InvalidOperationException, poiché in questo caso non si tratta di un tipo generico aperto. Vedere Marc Gravells answer per una spiegazione dei tipi generici aperti.

+2

Penso che sto cominciando a vedere la luce . Non sta dicendo che p.GetValue puo 'solo chiamare su un tipo per cui Type.IsGenericParameter è vero' ma piuttosto che la proprietà sottostante rappresentata da p, che in questo caso è DeclaringMethod, può essere chiamata solo è Type.IsGenericParameter è vero. – sgmoore

+1

Esattamente - questo è ciò che "l'eccezione è stata lanciata dalla destinazione di un'invocazione" significa che la "destinazione di un'invocazione" in questo caso è il getter della proprietà 'DeclaringMethod', e si otterrebbe la stessa eccezione di' IsGenericParameter' leggendo 'obj.DeclaringMethod' direttamente. – stevemegson

+0

Ho contrassegnato questa risposta come la mia risposta accettata in quanto è stata la più utile, ma in realtà la maggior parte delle altre risposte ha aiutato pure. Quindi grazie a tutti. – sgmoore

13

Così che cosa è esattamente un "tipo per il quale Type.IsGenericParameter è vero"

Questo significa che è un tipo di argomento generico in un tipo generico aperto - cioè dove non abbiamo scelto una T ancora; ad esempio:

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

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

Quindi; hai qualche generico aperto in giro? Forse se puoi dare un esempio di dove hai preso il tuo obj?

+0

L'OP indica il tipo di oggetto in System.RuntimeType – AnthonyWJones

+0

Da quello che posso vedere, il problema non è tanto l'esistenza di generici aperti, ma l'esistenza di tipi normali :) –

+0

Inizio con un'eccezione (in questo caso una SqlException) che viene passata alla mia routine di dump. La routine di dump è ricorsiva, quindi il dump viene richiamato su tutte le proprietà e i campi nell'eccezione. Ci sono alcuni generici nel mio codice, ma non generici aperti. Ciò che mi confonde è che l'errore sembra dire che p.GetValue funziona solo se GenericParameter è true, che chiaramente non è vero. – sgmoore

3

Tutti gli indizi ci sono. Il tipo di oggetto è la classe Type stessa (o piuttosto la strana derivata RuntimeType).

Al punto di errore è arrivato il ciclo Type classe proprietà DeclaringMethod. Tuttavia il tipo che questa istanza della classe Type sta descrivendo è System.Data.SqlClient.SqlConnection che non è un tipo generico di un metodo.

Di conseguenza, il tentativo di richiamare il risultato DeclaringMethod nell'eccezione.

La chiave è che si sta esaminando il tipo della classe Type. È un po 'circolare ma pensate a questo: -

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

Qual è la classe che descrive?

1

Come evitare questo errore senza try/catch?

Quasi non si può.Quando chiami p.GetValue, chiami il getter su quella proprietà, che potrebbe generare qualsiasi tipo di eccezione. Ad esempio, SqlConnection.ServerVersion genererà un'eccezione se la connessione è chiusa e dovrai gestirla.

Da dove vengono questi membri extra?

tuo obj contiene già l'oggetto che rappresenta RuntimeTypeSqlConnection, piuttosto che un esempio di SqlConnection. obj.GetMembers() restituirebbe i 65 membri della classe SqlConnection, ma chiamando di nuovo GetType(), otterrete i 188 membri di RuntimeType.

Che cos'è lo IsGenericParameter?

Invece di rappresentare una classe, è possibile avere un'istanza di RuntimeType che rappresenta un parametro generico a una classe o di un metodo (Il T e TOutput in List<T>.ConvertAll<TOutput>. In questo caso, DeclaringMethod sull'oggetto che rappresenta TOutput avrebbe lasciato che si ottiene un oggetto MethodInfo che rappresenta il metodo ConvertAll<>. Tuttavia, quando il RuntimeType rappresenta una classe, l'idea di un metodo di dichiarazione non ha alcun senso. Ecco perché la lettura della proprietà fa sì che l'eccezione che si è visto.