传递方法作为参数[重复]
Posted
技术标签:
【中文标题】传递方法作为参数[重复]【英文标题】:Pass method as parameter [duplicate] 【发布时间】:2014-04-22 16:57:42 【问题描述】:我在 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);
但是,我无法确定执行此操作所需的类型。
如何正确定义我的方法?
作为旁注,我很乐意将其写为库的扩展方法,但这并不完全适用于库的行为方式。
【问题讨论】:
你真的需要名字还是调用方法就够了?你想达到什么目的? 这个库是否有任何特定的签名?例如,是否所有方法都必须采用 0 参数并返回 void?或者图书馆是否能够调用你给它的任何方法? 这能回答你的问题吗? Pass Method as Parameter using C# 【参考方案1】:尝试像这样使用Action
或Func
:
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") );
【讨论】:
methodName
将设置为编译器生成的名称(即<EnclosingMethod>b__0
),而不是AnotherMethod
。
非常感谢卢卡斯!我完全忘记了方法有返回值,所以我需要使用 Func 而不是 Action。我不知道你可以这样使用委托。
不适用于 Windows。这个答案似乎取决于一个可以在没有通知的情况下更改的实现怪癖
@Vesuvian 你真的想编写只适用于特定编译器的代码吗?您应该:a) 仅传递方法组,或 b) 使用表达式来评估 lambda。不使用表达式从 lambda 检索方法名称是有风险的。
@Vesuvian 您是使用 lambda,就像这个答案一样,还是直接传递命名方法?后者将导致在此处提供方法的名称,而前者不应该。如果是这样,那将是不正确的。【参考方案2】:
如果您的库将方法限制为具有特定签名(例如,0 个参数,返回 void),那么您可以使用具有适当签名的委托。
例如,Action
表示返回 void 的参数为 0 的方法。
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);
【讨论】:
【参考方案3】:据我了解,问题不在于如何通过委托调用方法,而是如何在不借助魔术字符串的情况下获取方法的名称。
使用反射不会让魔法字符串消失。您可以获得具有特定签名的类型的所有方法,但您仍然需要知道您所针对的方法的名称。一个简单的重命名重构仍然会破坏你的代码。
在这种情况下,一个典型的解决方案是传递一个指向您想要的成员的表达式。这在 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);
【讨论】:
【参考方案4】:你需要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(SomeMethod);
methodName 设置为“SomeMethod”, 但是如果你使用匿名方法来调用它
OtherMethod(() => MessageBox.Show("The main thing ..."));
然后你会得到一个讨厌的编译器生成的名字,比如<YourNamespaceName_Load>b__0
【讨论】:
@dcastro 获取方法名很简单,看看更新的答案。methodName
将设置为编译器生成的名称(即<EnclosingMethod>b__0
),而不是AnotherMethod
。
@dcastro 如果我使用匿名方法?是的,如果我使用命名方法?没有。
这不是您的代码显示的内容。你的methodName
永远不会被设置为Show
。
您不认为这足够相关以包含在答案中吗?以上是关于传递方法作为参数[重复]的主要内容,如果未能解决你的问题,请参考以下文章