.net 通过反射调用带有可选参数的方法

ddrv8njm  于 2022-12-27  发布在  .NET
关注(0)|答案(6)|浏览(184)

我在使用带有可选参数的C#4.0时遇到了另一个问题。
如何调用一个不需要任何参数的函数(或者更确切地说是构造函数,我有ConstructorInfo对象)?
下面是我现在使用的代码:

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

(我刚刚尝试了不同的BindingFlags)。
GetParameterlessConstructor是我为Type编写的自定义扩展方法。

jqjz2hbq

jqjz2hbq1#

根据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);
7gcisfzg

7gcisfzg2#

可选参数由普通属性表示,并由编译器处理。
它们对IL没有任何影响(除了元数据标志),并且反射不直接支持它们(IsOptionalDefaultValue属性除外)。
如果你想在反射中使用可选参数,你需要手动传递它们的默认值。

gblwokeq

gblwokeq3#

我只是添加一些代码...因为。我同意,代码并不令人愉快,但它相当直接。希望这能帮助那些无意中遇到这个问题的人。它经过测试,尽管可能不如您在生产环境中所希望的那样好:
正在使用参数args调用对象obj上的方法methodName:

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调用)。

qncylg1j

qncylg1j4#

当您看到代码被反编译时,所有问题都消失了:
c编号:

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

特别代表:

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

正如您所看到的,可选参数是一个真正独立的实体,它用特定的属性装饰,并且在通过反射调用时必须相应地加以考虑,如前所述。

fdbelqdn

fdbelqdn5#

在开源框架ImpromptuInterface版本4中,你可以使用C# 4.0中的DLR来调用very late bound way中的构造函数,它完全知道带有命名/可选参数的构造函数,这比Activator.CreateInstance(Type type, params object[] args)快4倍,而且你不必反映默认值。

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"))
8xiog9wr

8xiog9wr6#

我知道这是一个老的线程,但只是想添加这个。如果你不确定这个方法有多少参数,你可以动态地做这个:

var method = obj.GetType().GetMethod("methodName");
int? parameters = method?.GetParameters().Length;
var data = method?.Invoke(prop, (object?[]?)(parameters.HasValue ? Enumerable.Repeat(Type.Missing, parameters.Value).ToArray() : Array.Empty<object>()));

希望这个有用。

相关问题