Invocation de méthodes avec des parameters facultatifs par reflection

J’ai rencontré un autre problème en utilisant C # 4.0 avec des parameters facultatifs.

Comment puis-je appeler une fonction (ou plutôt un constructeur, j’ai l’object ConstructorInfo ) pour lequel je sais qu’il ne nécessite aucun paramètre?

Voici le code que j’utilise maintenant:

 type.GetParameterlessConstructor() .Invoke(BindingFlags.OptionalParamBinding | BindingFlags.InvokeMethod | BindingFlags.CreateInstance, null, new object[0], CultureInfo.InvariantCulture); 

(Je viens d’essayer avec différents BindingFlags ).

GetParameterlessConstructor est une méthode d’extension personnalisée que j’ai écrite pour Type .

Selon MSDN , pour utiliser le paramètre par défaut, vous devez passer Type.Missing .

Si votre constructeur dispose de trois arguments facultatifs, au lieu de passer un tableau d’objects vide, vous passez un tableau d’objects à trois éléments dont la valeur est Type.Missing , par exemple

 type.GetParameterlessConstructor() .Invoke(BindingFlags.OptionalParamBinding | BindingFlags.InvokeMethod | BindingFlags.CreateInstance, null, new object[] { Type.Missing, Type.Missing, Type.Missing }, CultureInfo.InvariantCulture); 

Les parameters facultatifs sont désignés par un atsortingbut ordinaire et sont gérés par le compilateur.
Ils n’ont aucun effet (autre qu’un indicateur de métadonnées) sur l’IL et ne sont pas directement pris en charge par la reflection (sauf pour les propriétés IsOptional et DefaultValue ).

Si vous souhaitez utiliser des parameters facultatifs avec reflet, vous devez transmettre manuellement leurs valeurs par défaut.

Je vais juste append du code … parce que. Le code n’est pas agréable, j’en conviens, mais c’est assez simple. J’espère que cela aidera quelqu’un qui trébuche à travers cela. Il est testé, mais probablement pas aussi bien que vous le souhaiteriez dans un environnement de production:

Méthode appelante methodName sur object obj avec arguments args:

  public Tuple Evaluate(IScopeContext c, object obj, ssortingng methodName, object[] args) { // Get the type of the object var t = obj.GetType(); var argListTypes = args.Select(a => a.GetType()).ToArray(); var funcs = (from m in t.GetMethods() where m.Name == methodName where m.ArgumentListMatches(argListTypes) select m).ToArray(); if (funcs.Length != 1) return new Tuple(false, null); // And invoke the method and see what we can get back. // Optional arguments means we have to fill things in. var method = funcs[0]; object[] allArgs = args; if (method.GetParameters().Length != args.Length) { var defaultArgs = method.GetParameters().Skip(args.Length) .Select(a => a.HasDefaultValue ? a.DefaultValue : null); allArgs = args.Concat(defaultArgs).ToArray(); } var r = funcs[0].Invoke(obj, allArgs); return new Tuple(true, r); } 

Et la fonction ArgumentListMatches est ci-dessous, ce qui remplace fondamentalement la logique probablement trouvée dans GetMethod:

  public static bool ArgumentListMatches(this MethodInfo m, Type[] args) { // If there are less arguments, then it just doesn't matter. var pInfo = m.GetParameters(); if (pInfo.Length < args.Length) return false; // Now, check compatibility of the first set of arguments. var commonArgs = args.Zip(pInfo, (margs, pinfo) => Tuple.Create(margs, pinfo.ParameterType)); if (commonArgs.Where(t => !t.Item1.IsAssignableFrom(t.Item2)).Any()) return false; // And make sure the last set of arguments are actually default! return pInfo.Skip(args.Length).All(p => p.IsOptional); } 

Beaucoup de LINQ, et cela n’a pas été testé!

En outre, cela ne gérera pas les appels de fonction ou de méthode génériques. Cela le rend beaucoup plus laid (comme dans les appels GetMethod répétés).

Avec le framework opensource ImpromptuInterface à partir de la version 4, vous pouvez utiliser le DLR dans C # 4.0 pour appeler les constructeurs de manière très tardive et il est parfaitement conscient des constructeurs avec des arguments nommés / optionnels. Activator.CreateInstance(Type type, params object[] args) et vous n’avez pas à refléter les valeurs par défaut.

 using ImpromptuInterface; using ImpromptuInterface.InvokeExt; 

 //if all optional and you don't want to call any Impromptu.InvokeConstructor(type) 

ou

 //If you want to call one parameter and need to name it Impromptu.InvokeConstructor(type, CultureInfo.InvariantCulture.WithArgumentName("culture")) 

Toutes les questions disparaissent lorsque vous voyez votre code décompilé:

c #:

 public MyClass([Optional, DefaultParameterValue("")]ssortingng myOptArg) 

msil:

 .method public hidebysig specialname rtspecialname instance void .ctor([opt]ssortingng myOptArg) cil managed 

Comme vous le voyez, le paramètre optionnel est une véritable entité distincte décorée d’atsortingbuts spécifiques et qui doit être respectée lors de l’appel par reflection, comme décrit précédemment.