2010-03-11 12 views
49

Ich habe ein anderes Problem mit C# 4.0 mit optionalen Parametern aufgetreten.Aufrufen von Methoden mit optionalen Parametern durch Reflexion

Wie kann ich eine Funktion aufrufen (oder eher einen Konstruktor, ich habe das Objekt ConstructorInfo), für das ich weiß, dass es keine Parameter benötigt? Hier

ist der Code, den ich jetzt verwenden:

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

(Ich habe gerade versucht, mit verschiedenen BindingFlags).

GetParameterlessConstructor ist eine benutzerdefinierte Erweiterungsmethode, die ich für Type schrieb.

Antwort

106

Nach MSDN, den Standardparameter verwenden Sie Type.Missing passieren sollte.

Wenn Ihr Konstruktor über drei optionale Argumente verfügt, übergeben Sie anstelle eines leeren Objektarrays ein Objektarray mit drei Elementen, wobei der Wert jedes Elements Type.Missing ist, z.

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

Diese Antwort ist eigentlich besser als die, die als richtig markiert ist! –

+0

Ich kann bestätigen, dass das funktioniert. Ich stimme zu, dass dies in den meisten Fällen eine bessere Lösung ist und wahrscheinlich als solche gekennzeichnet werden sollte. – N8allan

+0

Kann man "Invoke" (ohne Parameter) einfach auf der resultierenden 'MethodInfo' oder' ConstructorInfo' aufrufen? – Alxandr

22

Optionale Parameter werden durch ein gewöhnliches Attribut gekennzeichnet und vom Compiler verarbeitet.
Sie haben (außer einem Metadaten-Flag) keine Auswirkungen auf die IL und werden von der Reflektion nicht direkt unterstützt (außer für die Eigenschaften IsOptional und DefaultValue).

Wenn Sie optionale Parameter mit Reflektion verwenden möchten, müssen Sie ihre Standardwerte manuell übergeben.

+0

Vielen Dank. Ich habe eine Lösung gefunden, die genau das tut. Es sieht nicht so hübsch aus, aber es funktioniert. Und auch, IsOptional ist nicht die einzige Eigenschaft, DefaultValue existiert auch, also baue ich einfach ein Array aus allen DefaultValues. – Alxandr

1

Mit der Open-Source-Framework ImpromptuInterface ab Version 4 können Sie das DLR in C# 4.0 verwenden können Konstrukteure in einem very late bound way aufzurufen und es ist völlig bewusst, Konstrukteure mit dem Namen/optionalen Argumenten, läuft die 4-mal schneller als Activator.CreateInstance(Type type, params object[] args) und Sie don‘ t müssen die Standardwerte widerspiegeln.

using ImpromptuInterface; 
using ImpromptuInterface.InvokeExt; 

...

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

oder

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

Ich werde nur etwas Code hinzufügen ... weil. Der Code ist nicht angenehm, stimme ich zu, aber es ist ziemlich geradlinig. Hoffentlich wird dies jemandem helfen, der darüber stolpert. Es wird getestet, wenn auch wahrscheinlich nicht so gut wie Sie in einer Produktionsumgebung wünschen würden:

Aufruf der Methode-Methode für Objekt obj mit Argumenten args:

public Tuple<bool, object> Evaluate(IScopeContext c, object obj, string 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<bool, object>(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<bool, object>(true, r); 
    } 

und die Funktion ArgumentListMatches unten ist, die im Grunde die Takes Stelle der Logik wahrscheinlich in GetMethod gefunden:

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

Viele LINQ, und dies nicht die Leistung getestet!

Dies wird auch keine generischen Funktions- oder Methodenaufrufe verarbeiten. Das macht dies wesentlich hässlicher (wie bei wiederholten GetMethod-Anrufen).

1

Alle Fragen verschwinden, wie Sie Ihren Code sehen dekompilierten:

C#:

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

msil:

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

Wie Sie sehen, optionaler Parameter ist eine echte eigenständige Einheit, die eingerichtet ist, mit spezifischen Attributen und muss entsprechend berücksichtigt werden, wenn über Reflektion aufgerufen wird, wie zuvor beschrieben.

Verwandte Themen