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

时间:2022-09-01 11:23:27

I've run into another problem using C# 4.0 with optional parameters.

我使用带有可选参数的C#4.0遇到了另一个问题。

How do I invoke a function (or rather a constructor, I have the ConstructorInfo object) for which I know it doesn't require any parameters?

我如何调用一个函数(或者更确切地说是构造函数,我有ConstructorInfo对象),我知道它不需要任何参数?

Here is the code I use now:

这是我现在使用的代码:

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

(I've just tried with different BindingFlags).

(我刚试过不同的BindingFlags)。

GetParameterlessConstructor is a custom extension method I wrote for Type.

GetParameterlessConstructor是我为Type编写的自定义扩展方法。

5 个解决方案

#1


111  

According to MSDN, to use the default parameter you should pass Type.Missing.

根据MSDN,要使用默认参数,您应该传递Type.Missing。

If your constructor has three optional arguments then instead of passing an empty object array you'd pass a three element object array where each element's value is Type.Missing, e.g.

如果您的构造函数有三个可选参数,那么您将传递一个三元素对象数组,而不是传递一个空对象数组,其中每个元素的值都是Type.Missing,例如,

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

#2


23  

Optional parameters are denoted by an ordinary attribute and are handled by the compiler.
They have no effect (other than a metadata flag) on the IL, and are not directly supported by reflection (except for the IsOptional and DefaultValue properties).

可选参数由普通属性表示,并由编译器处理。它们对IL没有影响(除元数据标志外),并且不直接受反射支持(IsOptional和DefaultValue属性除外)。

If you want to use optional parameters with reflection, you'll need to manually pass their default values.

如果要将可选参数与反射一起使用,则需要手动传递其默认值。

#3


3  

I'll just add some code... because. The code isn't pleasent, I agree, but it is fairly straight forward. Hopefully this will help someone who stumbles accross this. It is tested, though probably not as well as you would want in a production environment:

我只是添加一些代码...因为。我同意,代码并不令人满意,但这是相当直接的。希望这可以帮助那些偶然发现的人。它经过测试,但可能不如您在生产环境中所希望的那样好:

Calling method methodName on object obj with arguments args:

使用参数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);
    }

And the function ArgumentListMatches is below, which basically takes the place of the logic probably found in GetMethod:

并且函数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);
    }

Lots of LINQ, and this has not been performance tested!

很多LINQ,这还没有经过性能测试!

Also, this will not handle generic function or method calls. That makes this significantly more ugly (as in repeated GetMethod calls).

此外,这不会处理通用函数或方法调用。这使得这更加丑陋(如在重复的GetMethod调用中)。

#4


1  

With the opensource framework ImpromptuInterface as of version 4 you can use the DLR in C# 4.0 to invoke constructors in a very late bound way and it's totally aware of constructors with named/optional arguments, this runs 4 times faster than Activator.CreateInstance(Type type, params object[] args) and you don't have to reflect the default values.

使用开源框架ImpromptuInterface从版本4开始,您可以使用C#4.0中的DLR以非常晚的方式调用构造函数,并且它完全了解具有命名/可选参数的构造函数,这比Activator.CreateInstance(Type类型)快4倍。 ,params object [] args)并且您不必反映默认值。

using ImpromptuInterface;
using ImpromptuInterface.InvokeExt;

...

...

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

or

要么

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

#5


1  

All questions disappear as you see your code decompiled:

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

c#:

C#:

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

msil:

MSIL:

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

As you see, optional parameter is a real separate entity that is decorated with specific attributes and has to be respected accordingly when invoking via reflection, as described earlier.

如您所见,可选参数是一个真实的独立实体,它使用特定属性进行修饰,并且在通过反射调用时必须相应地进行调整,如前所述。

#1


111  

According to MSDN, to use the default parameter you should pass Type.Missing.

根据MSDN,要使用默认参数,您应该传递Type.Missing。

If your constructor has three optional arguments then instead of passing an empty object array you'd pass a three element object array where each element's value is Type.Missing, e.g.

如果您的构造函数有三个可选参数,那么您将传递一个三元素对象数组,而不是传递一个空对象数组,其中每个元素的值都是Type.Missing,例如,

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

#2


23  

Optional parameters are denoted by an ordinary attribute and are handled by the compiler.
They have no effect (other than a metadata flag) on the IL, and are not directly supported by reflection (except for the IsOptional and DefaultValue properties).

可选参数由普通属性表示,并由编译器处理。它们对IL没有影响(除元数据标志外),并且不直接受反射支持(IsOptional和DefaultValue属性除外)。

If you want to use optional parameters with reflection, you'll need to manually pass their default values.

如果要将可选参数与反射一起使用,则需要手动传递其默认值。

#3


3  

I'll just add some code... because. The code isn't pleasent, I agree, but it is fairly straight forward. Hopefully this will help someone who stumbles accross this. It is tested, though probably not as well as you would want in a production environment:

我只是添加一些代码...因为。我同意,代码并不令人满意,但这是相当直接的。希望这可以帮助那些偶然发现的人。它经过测试,但可能不如您在生产环境中所希望的那样好:

Calling method methodName on object obj with arguments args:

使用参数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);
    }

And the function ArgumentListMatches is below, which basically takes the place of the logic probably found in GetMethod:

并且函数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);
    }

Lots of LINQ, and this has not been performance tested!

很多LINQ,这还没有经过性能测试!

Also, this will not handle generic function or method calls. That makes this significantly more ugly (as in repeated GetMethod calls).

此外,这不会处理通用函数或方法调用。这使得这更加丑陋(如在重复的GetMethod调用中)。

#4


1  

With the opensource framework ImpromptuInterface as of version 4 you can use the DLR in C# 4.0 to invoke constructors in a very late bound way and it's totally aware of constructors with named/optional arguments, this runs 4 times faster than Activator.CreateInstance(Type type, params object[] args) and you don't have to reflect the default values.

使用开源框架ImpromptuInterface从版本4开始,您可以使用C#4.0中的DLR以非常晚的方式调用构造函数,并且它完全了解具有命名/可选参数的构造函数,这比Activator.CreateInstance(Type类型)快4倍。 ,params object [] args)并且您不必反映默认值。

using ImpromptuInterface;
using ImpromptuInterface.InvokeExt;

...

...

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

or

要么

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

#5


1  

All questions disappear as you see your code decompiled:

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

c#:

C#:

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

msil:

MSIL:

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

As you see, optional parameter is a real separate entity that is decorated with specific attributes and has to be respected accordingly when invoking via reflection, as described earlier.

如您所见,可选参数是一个真实的独立实体,它使用特定属性进行修饰,并且在通过反射调用时必须相应地进行调整,如前所述。