Oproepen van methoden met optionele parameters door reflectie

Ik ben een ander probleem tegengekomen bij het gebruik van C# 4.0 met optionele parameters.

Hoe roep ik een functie aan (of liever een constructor, ik heb het ConstructorInfo-object) waarvan ik weet dat er geen parameters voor nodig zijn?

Hier is de code die ik nu gebruik:

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

(Ik heb het net geprobeerd met verschillende BindingFlags).

GetParameterlessConstructoris een aangepaste extensiemethode die ik heb geschreven voor Type.


Antwoord 1, autoriteit 100%

Volgens MSDN, te gebruiken de standaardparameter die u moet doorgeven Type.Missing.

Als uw constructor drie optionele argumenten heeft, zou u in plaats van een lege objectarray door te geven een objectarray met drie elementen doorgeven waarbij de waarde van elk element Type.Missingis, bijvoorbeeld

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

Antwoord 2, autoriteit 17%

Optionele parameters worden aangeduid met een gewoon attribuut en worden afgehandeld door de compiler.
Ze hebben geen effect (behalve een metadata-vlag) op de IL en worden niet direct ondersteund door reflectie (behalve voor de eigenschappen IsOptionalen DefaultValue).

Als je optionele parameters met reflectie wilt gebruiken, moet je hun standaardwaarden handmatig doorgeven.


Antwoord 3, autoriteit 2%

Ik zal alleen wat code toevoegen… omdat. De code is niet prettig, daar ben ik het mee eens, maar het is vrij eenvoudig. Hopelijk helpt dit iemand die dit tegenkomt. Het is getest, maar waarschijnlijk niet zo goed als je zou willen in een productieomgeving:

Aanroepmethode methodName op object obj met 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);
    }

En de functie ArgumentListMatches staat hieronder, die in feite de plaats inneemt van de logica die waarschijnlijk in GetMethod wordt gevonden:

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

Veel LINQ, en dit is niet getest op prestaties!

Dit zal ook geen generieke functie- of methodeaanroepen afhandelen. Dat maakt dit aanzienlijk lelijker (zoals bij herhaalde GetMethod-aanroepen).


Antwoord 4

Alle vragen verdwijnen als je ziet dat je code gedecompileerd is:

c#:

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

msil:

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

Zoals je ziet, is optionele parameter een echt aparte entiteit die is versierd met specifieke attributen en dienovereenkomstig moet worden gerespecteerd bij het aanroepen via reflectie, zoals eerder beschreven.


Antwoord 5

Met het opensource framework ImpromptuInterfacevanaf versie 4 kun je de DLR gebruiken in C# 4.0 om constructors aan te roepen op een zeer late manieren het is volledig op de hoogte van constructors met benoemde/optionele argumenten, dit werkt 4 keer sneller dan Activator.CreateInstance(Type type, params object[] args)en u hoeft de standaardwaarden niet weer te geven.

using ImpromptuInterface;
using ImpromptuInterface.InvokeExt;

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

of

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

Other episodes