如何 select 使用反射基于参数类型的方法?

How to select a method based on parameter types using reflection?

我有一个参数数组,我需要调用一个实例方法,该方法应该具有最适合调用的签名,并在我的参数数组中给出参数的数量和类型。我在 Whosebug 上的类似问题上很难找到答案,这是因为我所针对的方法没有任何具体名称;相反,我必须 select 来自用特定属性修饰的方法列表中的一个方法。

包含一些可以调用的方法的 class 示例:

 public class TestClass
    {

        [MyAttribute]
        public void DoStuff()
        {
            // Do something
        }

        [MyAttribute]
        public void DoMoreStuff(string msg)
        {
            // Do something
        }

        [MyAttribute]
        public void DoEvenMoreStuff(string msg, int count, bool isCool = true)
        {
            // Do Something
        }

        [MyAttribute]
        public void DoEvenMoreStuff(object obj, int count, bool isCool = true)
        {
            // Do Something
        }

    }

现在,我需要能够调用用 MyAttribute 修饰的方法之一,但我事先不知道这些方法的名称。我只需要根据我已有的参数数组获取所有用 MyAttribute 和 select 装饰的方法;然后调用 selected 方法。

我应该怎么做才能select调用最好的方法?

我不确定我是否完全理解你,(如果我理解的话)你显然已经意识到严重缺陷这种方法有

但是,仅用于学术目的。你可以这样做

var argList = new object[] {"asd", 123, true };

var argTypes = argList.Select(x => x.GetType())
                           .ToList();

var testClass = new TestClass();

var method = testClass.GetType()
                      .GetMethods()
                      .Where(m => m.GetCustomAttributes(typeof(MyAttribute), false)
                                              .Length > 0)
                      .FirstOrDefault(x => x.GetParameters()
                                              .Select(y => y.ParameterType)
                                              .SequenceEqual(argTypes));

if (method != null)
{
   method.Invoke(testClass, argList);
}

Demo here


如前所述,更好的方法是使用

Binder.SelectMethod Method (BindingFlags, MethodBase[], Type[], ParameterModifier[])

Selects a method from the given set of methods, based on the argument type.

您可能想在默认活页夹 (Type.DefaultBinder) 上使用 Binder.SelectMethod。这将处理标准的隐式转换(例如到基本类型、接口或对象)以及一些数字转换(例如 int 到 double 和 long)。有关更详尽的描述,请参阅 documentation。这将不匹配 optional/default 参数。因此,如果您传递 string 和 int,它将不会与您示例中的第三个方法匹配,第三个布尔参数的默认值。请注意,您可能还想处理当可以选择多个候选方法时抛出的 AmbiguousMatchException。其使用的简短演示:

static void Main(string[] args)
{
    var methods = typeof(TestClass).GetMethods()
        .Where(mi => mi.GetCustomAttributes(true).OfType<MyAttribute>().Any()).ToArray();

    var flags = BindingFlags.Default; // I did not see a difference with BindingFlags.OptionalParamBinding;

    Type[][] cases = {
        new Type[0],
        new[] { typeof(string) },
        new[] { typeof(string), typeof(int) },
        new[] { typeof(string), typeof(int), typeof(bool) },
        new[] { typeof(int), typeof(int), typeof(bool) }
    };

    foreach (var typeCase in cases)
    {
        string desc = "(" + string.Join(",", typeCase.Select(t => t?.Name ?? "<null>")) + ")";

        var method = Type.DefaultBinder.SelectMethod(flags, methods, typeCase, null);

        string result = method?.ToString() ?? "No matching method found";
        Console.WriteLine($"{desc} -> {result}");
    }
}

输出:

() -> Void DoStuff()
(String) -> Void DoMoreStuff(System.String)
(String,Int32) -> No matching method found
(String,Int32,Boolean) -> Void DoEvenMoreStuff(System.String, Int32, Boolean)
(Int32,Int32,Boolean) -> Void DoEvenMoreStuff(System.Object, Int32, Boolean)