传递方法作为参数

时间:2014-03-17 13:10:36

标签: c# reflection methods

我在C#中使用库,其中一个方法要求我将目标方法的字符串名称作为参数传递。

我想避免使用硬编码字符串,原因显而易见,因此我将编写一个中间的util方法,它接受一个方法,获取名称(可能是通过反射)并将其提供给库方法。

我希望中间方法看起来像这样:

public void CallOtherMethod(???? inputMethod)
{
    string methodName = inputMethod.Name; // This gives me the method without the namespace, right?
    this.CallFinalMethod(methodName);
}

这样称呼:

this.CallOtherMethod(this.SomeOtherMethod);

但是,我在确定执行此操作所需的类型时遇到了一些麻烦。

如何正确定义我的方法?

作为旁注,我很乐意将其作为库的扩展方法编写,但这与库的行为方式不相符。

4 个答案:

答案 0 :(得分:3)

如果您的库限制方法具有特定签名(例如,0参数,返回void),那么您可以使用具有适当签名的委托。

例如,Action表示具有0参数的方法,该参数返回void。

public void CallOtherMethodMethodGroup(Action action)
{
    MethodInfo method = action.Method;

    //check that 'action' is not a compiler generated lambda
    if (!method.IsDefined(typeof (CompilerGeneratedAttribute)))
    {
        Console.WriteLine(method.Name);
    }
    else
        throw new InvalidOperationException("Use 'CallOtherMethodExpr' instead.");
}

并使用method group

进行调用
CallOtherMethodMethodGroup(AnotherMethod);

如果您的库接受带有任何类型签名的方法,那么使用您必须传入lambda而不是方法组:

CallOtherMethodExpr(() => AnotherMethod("dummy", "arg"));

但是,使用lambda时,您必须更改方法以接受Expression<Action>而不是Action。表达式可以被解释,代表可以解释。

public void CallOtherMethodExpr(Expression<Action> expr)
{
    string methodName;

    if (expr.Body.NodeType == ExpressionType.Call)
    {
        var call = expr.Body as MethodCallExpression;
        methodName = call.Method.Name;
    }
    else
        throw new InvalidOperationException("Expression must contain a method call");

    Console.WriteLine(methodName);
}

答案 1 :(得分:1)

尝试使用ActionFunc,如下所示:

public void CallOtherMethod(Action method)
{
    string methodName = method.Method.Name;
    method.Invoke();
}

 public void AnotherMethod(string foo, string bar)
{
    // Do Something
}

呼叫:

CallOtherMethod( () => AnotherMethod("foo", "bar") );

答案 2 :(得分:0)

据我所知,问题不在于如何通过委托来调用方法,而是如何在不使用魔术字符串的情况下获取方法的名称。

使用反射并不会使魔术字符串消失。您可以获取具有特定签名的所有类型的方法,但您仍需要知道要定位的方法的名称。简单的重命名重构仍会破坏您的代码。

在这种情况下,典型的解决方案是传递指向所需成员的Expression。这在MVVM框架中广泛使用,以传递任何已修改参数的名称

以下方法将检查提供的表达式是否为实际方法调用并提取方法的名称:

    public static bool TryGetName(Expression<Action> expr, out string memberName)
    {
        memberName = null;
        var body = expr.Body as MethodCallExpression;
        if (body == null)
            return false;
        memberName = body.Method.Name;
        return true;
    }

可以像这样调用该方法:

bool success=TryGetName(()=>someObject.SomeMethod(), out name);

所有Action和Func对象都从Action继承,这意味着SomeMethod实际上可能是一个函数。如果方法包含参数,则调用将需要一些虚拟值:

bool success=TryGetName(()=>someObject.SomeMethod(null,"dummy",0), out name);

答案 3 :(得分:-2)

您需要Delegate

public void OtherMethod(Action method)
{
   MessageBox.Show("I am raised first.");
   string methodname = method.Method.Name;
   method.Invoke();
}

public void SomeMethod()
{
    some code ...
}

如果使用命名方法调用OtherMethod

OtherMethod(SomeMethod);

methodName设置为“SomeMethod”,
但是如果你使用匿名方法来调用它

OtherMethod(() => MessageBox.Show("The main thing ..."));

然后你会得到一个讨厌的编译器生成的名称,如<YourNamespaceName_Load>b__0