web-dev-qa-db-fra.com

La méthode ne peut être appelée que sur un type pour lequel Type.IsGenericParameter est true

Je reçois cette erreur sur une routine qui utilise la réflexion pour vider certaines propriétés d'objet, quelque chose comme le code ci-dessous.

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'erreur réelle est "Une exception a été émise par la cible d'une invocation" Avec une exception interne de "La méthode ne peut être appelée que sur un Type pour lequel Type.IsGenericParameter est true."

À ce stade du débogueur, obj apparaît sous la forme 

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

avec le type System.RuntimeType

La méthode m est {System.Reflection.MethodBase DeclaringMethod} 

Notez que obj est de type System.RuntimeType et members contient 188 éléments, alors qu'un simple Type (System.Data.SqlClient.SqlConnection) .GetMembers (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance ) ne retourne que 65.

J'ai essayé de vérifier isGenericParameter sur obj et p.PropertyType, mais cela semble être faux pour la plupart des propriétés, y compris celles où p.GetValue fonctionne.

Alors qu'est-ce exactement qu'un "Type pour lequel Type.IsGenericParameter est true" et, plus important encore, Comment éviter cette erreur sans essayer/intercepter?

19
sgmoore

Tout d'abord, vous vous êtes fondé sur une hypothèse incorrecte, c'est-à-dire que vous avez supposé que members a renvoyé les membres d'une instance de System.Data.SqlClient.SqlConnection, ce qui n'est pas le cas. Ce qui a été retourné sont les membres d'une instance de System.Type.

De la documentation MSDN pour DeclaringType :

Obtenir la propriété DeclaringMethod Sur un type dont la propriété IsGenericParameter Est false renvoie un InvalidOperationException.

Alors ... il est compréhensible qu'une InvalidOperationException soit lancée, car naturellement, vous n'avez pas affaire à un type générique ouvert ici. Voir Marc Gravells répond pour une explication des types génériques ouverts.

13
Eric Smith

Alors, qu'est-ce qu'un "Type pour lequel Type.IsGenericParameter est true" 

Cela signifie que c’est un argument de type générique dans un type générique ouvert - c’est-à-dire où nous n’avons pas encore choisi de variable T; par exemple:

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

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

Alors; Avez-vous des génériques ouverts qui traînent? Peut-être si vous pouvez donner un exemple d'où vous avez obtenu votre obj?

14
Marc Gravell

Tous les indices sont là. Le type d'obj est la classe Type (ou plutôt le dérivé étrange RuntimeType).

Au moment de l'échec, votre boucle est arrivée à la propriété de la classe Type appelée DeclaringMethod . Toutefois, le type décrit par cette instance de la classe Type est System.Data.SqlClient.SqlConnection, qui n'est pas un type générique d'une méthode.

Par conséquent, tenter d'invoquer le get sur DeclaringMethod résulte en une exception.

La clé est que vous examinez le type de la classe Type. C'est un peu circulaire, mais pensez à ceci: -

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

Qu'est-ce que la classe ouch décrit?

3
AnthonyWJones

Comment éviter cette erreur sans essayer/attraper?

Vous ne pouvez certainement pas. Lorsque vous appelez p.GetValue, vous appelez le getter sur cette propriété, ce qui peut générer n'importe quel type d'exception. Par exemple, SqlConnection.ServerVersion lève une exception si la connexion est fermée et vous devez le gérer.

D'où viennent ces membres supplémentaires?

Votre obj contient déjà l'objet RuntimeType représentant SqlConnection, plutôt qu'une instance de SqlConnection. obj.GetMembers() renverrait les 65 membres de la classe SqlConnection, mais en appelant à nouveau GetType(), vous obtiendrez les 188 membres de RuntimeType.

Qu'est-ce que IsGenericParameter?

Au lieu de représenter une classe, vous pouvez avoir une instance de RuntimeType qui représente un paramètre générique pour une classe ou une méthode (Les T et TOutput dans List<T>.ConvertAll<TOutput>. Dans ce cas, DeclaringMethod sur l'objet représentant TOutput vous permettrait d'obtenir un objet MethodInfo représentant Méthode ConvertAll<>. Cependant, lorsque la variable RuntimeType représente une classe, l'idée d'une méthode de déclaration n'a aucun sens. C'est pourquoi la lecture de la propriété provoque l'exception que vous avez vue.

1
stevemegson

Mon problème a été résolu en supprimant des champs et des tables répétitifs dans mon modèle et commenté en définissant une requête et un magasin supprimé: dans le fichier XML Model.edmx.

1
aseman arabsorkhi