Вызов методов с необязательными параметрами через отражение


я столкнулся с другой проблемой, используя C# 4.0 с дополнительными параметрами.

как вызвать функцию (или, скорее, конструктор, у меня есть ConstructorInfo объект) для которого я знаю, что он не требует никаких параметров?

вот код, который я использую сейчас:

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

(Я только что пробовал с разных BindingFlags).

GetParameterlessConstructor это пользовательский метод расширения, который я написал для Type.

5 52

5 ответов:

По данным MSDN, чтобы использовать параметр по умолчанию, вы должны пройти Type.Missing.

Если ваш конструктор имеет три необязательных аргумента, то вместо передачи пустого массива объектов вы передадите трехэлементный массив объектов, где значение каждого элемента Type.Missing, например,

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

необязательные параметры обозначаются обычным атрибутом и обрабатываются компилятором.
Они не имеют никакого эффекта (кроме флага метаданных) на IL и не поддерживаются непосредственно отражением (за исключением IsOptional и DefaultValue свойства).

Если вы хотите использовать дополнительные параметры с отражением, вам нужно будет вручную передать их значения по умолчанию.

Я просто добавлю код... потому что. Код не очень приятен, я согласен, но это довольно прямолинейно. Надеюсь, это поможет кому-то, кто спотыкается об это. Он протестирован, хотя, вероятно, не так хорошо, как вы хотели бы в производственной среде:

вызов метода methodName на объект obj с аргументами 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);
    }

и функция ArgumentListMatches находится ниже, что в основном занимает место логики, вероятно, найденной в 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);
    }

много LINQ, и это не было проверено на производительность!

кроме того, это не будет обрабатывать общие функции или вызовы методов. Это делает это значительно более уродливым (как в повторных вызовах GetMethod).

С открытым исходным кодом framework импровизированный интерфейс начиная с версии 4 Вы можете использовать DLR в C# 4.0 для вызова конструкторов в очень привязана способом и он полностью осведомлен о конструкторах с именованными / необязательными аргументами, это работает в 4 раза быстрее, чем Activator.CreateInstance(Type type, params object[] args) и вы не должны отражать значения по умолчанию.

using ImpromptuInterface;
using ImpromptuInterface.InvokeExt;

...

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

или

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

все вопросы исчезают, как вы видите ваш код декомпилирован:

c#:

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

msil:

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