是否可以将任意方法组作为参数传递给方法?

时间:2009-12-24 16:15:15

标签: c#

我想写一个类似下面的函数

// The type 'MethodGroup' below doesn't exist.  This is fantasy-code.
public void MyFunction(MethodGroup g)
{
    // do something with the method group
}

稍后,我可以使用任何方法组来呼叫MyFunction。这样的事情。

MyFunction(Object.Equals)

如果我承诺签名,那么事情就可以了。

public void MyFunction(Func<object, object, bool> f)
{
    // do something with known delegate
}
...
MyFunction(Object.Equals)

方法组Object.Equals很高兴被强制转换为已知的委托类型Func<object, object, bool>,但我不想提交特定的签名。我想将任何方法组传递给MyFunction

方法组无法转换为System.Object

public void MyFunction(object o)
{
    // do something with o
}
...
MyFunction(Object.Equals) // doesn't work

我认为每个人都忘记了方法调用的大括号,并在某些时候发现了这一点。我希望这并不意味着方法组不会(或不能转换)为第一类对象。

我不认为Linq表达会给出我正在寻找的那种普遍性,但我当然可能会遗漏一些东西。

我还应该提一下,如果方法组包含重载会很好,前提是我有办法检查方法组。

我如何处理方法组? 我可以打印组中所有方法的所有签名(重载,扩展方法等),或者我可以“调用”具有一些参数的组(如果可能,让它解析为组中的正确重载)。还有其他方法可以做这些事情,但它们是您可能想要对方法组执行的一些操作。

正如有几个人提到的那样,我可以接受Delegate,并在致电MyFunction时转换为特定的已知代理类型。

public void MyFunction(Delegate d)
{
    // do something with d
}
...
MyFunction((Func<object, object, bool>)Object.Equals)

但这与传递整个方法组并不完全相同。这将从组中选择一个方法并将其转换为特定的委托。我真的想一次性通过整个小组。

4 个答案:

答案 0 :(得分:8)

我认为这里的一个重要问题是:如果你可以通过一个方法组,你会怎么做?

请记住,方法组只是一组方法的名称 - 可能有一个或多个 - 它们可能是重载或扩展方法。方法组不是在编译代码中保留的概念 - 编译器支持从方法组到委托的转换 - 但为了使其成为可能,编译器必须能够毫不含糊地解决您想要的方法通过。

如果函数明确定义了方法的签名,则方法组仅作为函数的参数有效。因此,例如:public void MyFunction( Func<int> f )可以传递给方法组。

你最接近的是写一个接受代表类型的函数:

public void MyFunction( Delegate d ) { ... }

但您仍然无法传递方法组,因为没有从方法组到Delegate的转换。您必须转换为特定的代理签名:

// call MyFunction with a particular delegate
MyFunction( (Func<string>)myObj.ToString );  // method group conversion may occur

答案 1 :(得分:7)

您可以传递委托:

public void MyFunction(Delegate d)
{
    // do something with the method group
}

但是,在调用方法时,您必须指定委托类型:

MyFunction(new Func<object, object, bool>(Object.Equals));

注意:上面的代码不是传递方法组而是传递单个方法......我认为不可能传递方法组。你为什么要那样做?

答案 2 :(得分:1)

我认为你可以使用这样的签名:

MyFunc<T>(EventHandler<T> newDelegate)
{
    object storedDelegate = newDelegate;
    // after that you can use storedDelegate later
}

答案 3 :(得分:1)

请求者之所以想执行此操作,是因为他想要有关该方法组的元数据与该组中的任何方法(例如名称)相同,或者他想对该组中的所有方法进行处理。我相信nameof (C# Reference)是最有用的方法。

我按照他最初的预期来定义MyFunction,但是需要注意的是,这仅适用于一个方法签名。如果MethodInfo提供了您所需的有关该功能的所有信息,则实际上可能不需要调用它。

public void MyFunction(Delegate d)
{
    // do something with d
}

然后,我们将提供一个包装,该包装采用methodName,类型和(可选)调用对象。即使是实例方法,调用对象也不需要 来生成委托,但是如果该委托对象为null,则实际上执行委托可能会失败。即使为创建委托,为静态方法提供调用对象也将导致绑定错误。我在下面编写了返回静态和实例方法并将任何实例方法绑定到调用方的方法,而静态方法绑定到null。您可以修改它以在调用方是实例时跳过静态方法,反之亦然。

public void MyFunctionMethodGroup(string methodName, Type type, object caller)
{
    var methods = type.GetMethods().Where(x => x.Name == methodName);
    foreach(var method in methods)
    {
        Delegate myDelegate = method.CreateDelegate(Expression.GetDelegateType(
            method.GetParameters().Select(x => x.ParameterType)
            .Concat(new[] { method.ReturnType })
            .ToArray()), method.IsStatic ? null : caller);
        MyFunction(myDelegate);
    }
}

以下是一些示例。

静态方法

MyFunctionMethodGroup(nameof(String.IsNullOrEmpty), typeof(String), null);

实例方法

请注意,即使它是实例函数,也可以从任何一个类名调用函数。如果您不需要调用该方法并且没有调用者,则此方法非常有用。您也可以从实例中调用它,而无需显式编写类名。

string xyz = "2";
MyFunctionMethodGroup(nameof(String.Contains), typeof(String), xyz);
MyFunctionMethodGroup(nameof(xyz.Contains), xyz.GetType(), xyz);

高级案例

扩展方法

值得记住的是,扩展方法在定义扩展方法的名称空间上被称为静态方法。如果您需要实例参数来实现在MyFunction中要执行的操作,则必须将其作为一个新参数传递,该参数与调用方分开,因为扩展方法是静态的,因此为null。

通用方法

此外,上面的代码不适用于通用方法。这是应用方法的通用类型的示例。

   public void MyFunctionMethodGroup(string methodName, Type type, object caller = null, params Type[] genericParameters)
    {
        //GetMethods only returns public methods. If you need protected/private, modify as appropriate.
        var methods = type.GetMethods().Where(x => x.Name == methodName);
        foreach (var method1 in methods)
        {
            MethodInfo method = method1.IsGenericMethod ? method1.MakeGenericMethod(genericParameters) : method1;
            Type delegateType = Expression.GetDelegateType(
                method.GetParameters().Select(x => x.ParameterType)
                .Concat(new[] { method.ReturnType })
                .ToArray());
            Delegate myDelegate = method.CreateDelegate(delegateType, method.IsStatic ? null : caller);
            MyFunction(myDelegate);
        }
    }

通用类

如果传入的类没有在Type参数中定义的泛型类型,则上述方法可能无法完全支持泛型类中的方法。

输出/参考参数

编写的代码不支持输出或参考参数。它也可能不支持ref参数。