C# 在使用反射调用方法时处理空参数

C# 在使用反射调用方法时处理空参数,c#,reflection,null,C#,Reflection,Null,我试图编写代码,从参数列表中推断类型,然后调用与这些参数匹配的方法。这非常有效,除非参数列表中有null值 我想知道如何使Type.GetMethod调用匹配函数/重载,即使参数列表中有null参数 object CallMethodReflection(object o, string nameMethod, params object[] args) { try { var types = TypesFromObjects(args); var

我试图编写代码,从参数列表中推断类型,然后调用与这些参数匹配的方法。这非常有效,除非参数列表中有
null

我想知道如何使
Type.GetMethod
调用匹配函数/重载,即使参数列表中有
null
参数

object CallMethodReflection(object o, string nameMethod, params object[] args)
{
    try
    {
        var types = TypesFromObjects(args);
        var theMethod = o.GetType().GetMethod(nameMethod, types);
        return (theMethod == null) ? null : theMethod.Invoke(o, args);
    }
    catch (Exception ex)
    {
        return null;
    }
}
Type[] TypesFromObjects(params object[] pParams)
{
    var types = new List<Type>();
    foreach (var param in pParams)
    {
        types.Add((param == null) ? null : param.GetType());
    }
    return types.ToArray();
}

主要是,我试图确定如何更改代码,以便第
/*2*/
行也能正常工作。

我认为您必须执行以下操作:

var methods = o.GetType().GetMethods().Where(m => m.Name == methodName);

然后基本上做你自己的过载解决方案。您可以先尝试现有方法,捕获异常,然后尝试上述操作。

对于任何空参数,您可以只匹配任何引用类型。以下非常简单/朴素的代码适用于您的方法,如图所示,但它不能处理歧义异常或使用ref/out参数的更复杂情况,也不能将派生类型传递给方法或泛型方法

如果您使用的是4.0,那么简单地使用dynamic可能是更好的选择

object CallMethodReflection(object o, string nameMethod, params object[] args)
{
    try
    {
        var types = TypesFromObjects(args);
        var oType = o.GetType();
        MethodInfo theMethod = null;

        // If any types are null have to perform custom resolution logic
        if (types.Any(type => type == null)) 
        {
            foreach (var method in oType.GetMethods().Where(method => method.Name == nameMethod))
            {
                var parameters = method.GetParameters();

                if (parameters.Length != types.Length)
                    continue;

                //check to see if all the parameters match close enough to use
                bool methodMatches = true;
                for (int paramIndex = 0; paramIndex < parameters.Length; paramIndex++)
                {
                    //if arg is null, then match on any non value type
                    if (args[paramIndex] == null)
                    {
                        if (parameters[paramIndex].ParameterType.IsValueType)
                        {
                            methodMatches = false;
                            break;
                        }
                    }
                    else //otherwise match on exact type, !!! this wont handle things passing a type derived from the parameter type !!!
                    {
                        if (parameters[paramIndex].ParameterType != args[paramIndex].GetType())
                        {
                            methodMatches = false;
                            break;
                        }
                    }
                }

                if (methodMatches)
                {
                    theMethod = method;
                    break;
                }
            }
        }
        else
        {
            theMethod = oType.GetMethod(nameMethod, types);
        }

        Console.WriteLine("Calling {0}", theMethod);
        return theMethod.Invoke(o, args);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Could not call method: {0}, error: {1}", nameMethod, ex.ToString());
        return null;
    }
}
object CallMethodReflection(object o,string namethod,params object[]args)
{
尝试
{
变量类型=类型来自对象(args);
var oType=o.GetType();
MethodInfo theMethod=null;
//如果任何类型为null,则必须执行自定义解析逻辑
if(types.Any(type=>type==null))
{
foreach(oType.GetMethods()中的var方法,其中(method=>method.Name==nameMethod))
{
var parameters=method.GetParameters();
if(parameters.Length!=types.Length)
继续;
//检查所有参数是否匹配到足以使用
bool methodMatches=true;
对于(int paramIndex=0;paramIndex
对GetMethod调用有重写,这些重写接受从Binder类派生的对象。这允许您重写默认方法绑定,并根据传递的实际参数返回要使用的方法。这基本上就是其他两个答案所做的。这里有一些示例代码:

多亏了以及一些和,我已经尝试实现了我自己的解决方案,到目前为止,该解决方案一直在为我工作

我创建了一个类,该类继承了
绑定器
类,并将处理潜在
null
参数/类型的逻辑放在其中

object CallMethodReflection(object o, string nameMethod, params object[] args)
{
    try
    {
        var types = TypesFromObjects(args);
        var theMethod = o.GetType().GetMethod(nameMethod, CustomBinder.Flags, new CustomBinder(), types, null);
        return (theMethod == null) ? null : theMethod.Invoke(o, args);
    }
    catch (Exception ex)
    {
        return null;
    }
}
Type[] TypesFromObjects(params object[] pParams)
{
    var types = new List<Type>();
    foreach (var param in pParams)
    {
        types.Add((param == null) ? typeof(void) : param.GetType()); // GetMethod above doesn't like a simply null value for the type
    }
    return types.ToArray();
}
private class CustomBinder : Binder
{
    public const BindingFlags Flags = BindingFlags.Public | BindingFlags.Instance;
    public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] matches, Type[] types, ParameterModifier[] modifiers)
    {
        if (matches == null)
            throw new ArgumentNullException("matches");
        foreach (var match in matches)
        {
            if (MethodMatches(match.GetParameters(), types, modifiers))
                return match;
        }
        return Type.DefaultBinder.SelectMethod(bindingAttr, matches, types, modifiers); // No matches. Fall back to default
    }
    private static bool MethodMatches(ParameterInfo[] parameters, Type[] types, ParameterModifier[] modifiers)
    {
        if (types.Length != parameters.Length)
            return false;
        for (int i = types.Length - 1; i >= 0; i--)
        {
            if ((types[i] == null) || (types[i] == typeof(void)))
            {
                if (parameters[i].ParameterType.IsValueType)
                    return false; // We don't want to chance it with a wonky value
            }
            else if (!parameters[i].ParameterType.IsAssignableFrom(types[i]))
            {
                return false; // If any parameter doesn't match, then the method doesn't match
            }
        }
        return true;
    }
}

一个没有提到的选项是使用,一个旨在使反射任务更容易和更快(通过IL生成)的库

要调用给定命名参数字典(或具有应用作参数的属性的对象)的方法,可以如下所示调用最佳匹配:

obj.TryCallMethod( "SomeMethod", argsDictionary );
obj.TryCallMethod( "AnotherMethod", new { Foo = "Bar" } );
如果只有参数值及其顺序,则可以使用另一个重载:

obj.TryCallMethodWithValues( "MyMethod", 42, "foo", "bar", null, 2.0 );
PS:您需要从源代码管理获取最新的位,以利用TryCallMethodWithValues扩展


免责声明:我是Fasterflect项目的参与者。

我没有测试它,我认为其他答案更好,但我想知道为什么这不起作用:

foreach (var param in pParams.Where(p => p != null)
{
    types.Add(param.GetType());
}

您可以通过实现自己的GetMethod来解决这个问题,该GetMethod迭代对象中的所有方法,并确定哪一个是最佳匹配的,我希望这会有所帮助。 我用您提供的示例测试了下面的方法,它很有效

MethodInfo SmarterGetMethod(object o, string nameMethod, params object[] args)
{
var methods = o.GetType().GetMethods();
var min = args.Length;
var values = new int[methods.Length];
values.Initialize();
//Iterates through all methods in o
for (var i = 0; i < methods.Length; i += 1)
{
    if (methods[i].Name == nameMethod)
    {
        var parameters = methods[i].GetParameters();
        if (parameters.Length == min)
        {
            //Iterates through parameters
            for (var j = 0; j < min; j += 1)
            {
                if (args[j] == null)
                {
                    if (parameters[j].ParameterType.IsValueType)
                    {
                        values[i] = 0;
                        break;
                    }
                    else
                    {
                        values[i] += 1;
                    }
                }
                else
                {
                    if (parameters[j].ParameterType != args[j].GetType())
                    {
                        values[i] = 0;
                        break;
                    }
                    else
                    {
                        values[i] += 2;
                    }
                }
            }
            if (values[i] == min * 2) //Exact match                    
                return methods[i];
        }
    }
}

var best = values.Max();
if (best < min) //There is no match
    return null;
//Iterates through value until it finds first best match
for (var i = 0; i < values.Length; i += 1)
{
    if (values[i] == best)
        return methods[i];
}
return null; //Should never happen
}
MethodInfo智能目标方法(对象o、字符串名称方法、参数对象[]args)
{
var methods=o.GetType().GetMethods();
var min=参数长度;
var值=新的int[methods.Length];
初始化();
//迭代o中的所有方法
对于(var i=0;iforeach (var param in pParams.Where(p => p != null)
{
    types.Add(param.GetType());
}
MethodInfo SmarterGetMethod(object o, string nameMethod, params object[] args)
{
var methods = o.GetType().GetMethods();
var min = args.Length;
var values = new int[methods.Length];
values.Initialize();
//Iterates through all methods in o
for (var i = 0; i < methods.Length; i += 1)
{
    if (methods[i].Name == nameMethod)
    {
        var parameters = methods[i].GetParameters();
        if (parameters.Length == min)
        {
            //Iterates through parameters
            for (var j = 0; j < min; j += 1)
            {
                if (args[j] == null)
                {
                    if (parameters[j].ParameterType.IsValueType)
                    {
                        values[i] = 0;
                        break;
                    }
                    else
                    {
                        values[i] += 1;
                    }
                }
                else
                {
                    if (parameters[j].ParameterType != args[j].GetType())
                    {
                        values[i] = 0;
                        break;
                    }
                    else
                    {
                        values[i] += 2;
                    }
                }
            }
            if (values[i] == min * 2) //Exact match                    
                return methods[i];
        }
    }
}

var best = values.Max();
if (best < min) //There is no match
    return null;
//Iterates through value until it finds first best match
for (var i = 0; i < values.Length; i += 1)
{
    if (values[i] == best)
        return methods[i];
}
return null; //Should never happen
}