很容易声明一个方法,它将方法名作为字符串:
public void DoSomethingWithMethodName(string methodName)
{
// Do something with the method name here.
}
并将其调用为:
DoSomethingWithMethodName(nameof(SomeClass.SomeMethod));
我想摆脱nameof
并调用其他一些方法,如下所示:
DoSomethingWithMethod(SomeClass.SomeMethod);
然后能够获得与上面示例中相同的方法名称。使用一些Expression
和/或Func
魔法“感觉”可以做到这一点。问题是这个DoSomethingWithMethod
应该有什么签名,它应该做什么!
====================================
这个问题似乎引起了很多困惑,答案假设了我没有问的问题。这是一个提示,我的目标是什么,但不能正确。这是针对一些不同的问题(我有一个解决方案)。我可以声明:
private async Task CheckDictionary(Expression<Func<LookupDictionary>> property, int? expectedIndex = null)
{
await RunTest(async wb =>
{
var isFirst = true;
foreach (var variable in property.Compile().Invoke())
{
// Pass the override value for the first index.
await CheckSetLookupIndex(wb, GetPathOfProperty(property), variable, isFirst ? expectedIndex : null);
isFirst = false;
}
});
}
GetPathOfProperty
出自:https://www.automatetheplanet.com/get-property-names-using-lambda-expressions/和Fully-qualified property name
然后使用:
[Fact]
public async Task CommercialExcelRaterService_ShouldNotThrowOnNumberOfStories() =>
await CheckDictionary(() => EqNumberOfStories, 2);
其中,EqNumberOfStories
是:
public static LookupDictionary EqNumberOfStories { get; } = new LookupDictionary(new Dictionary<int, string>
{
{ 1, "" },
{ 2, "1 to 8" },
{ 3, "9 to 20" },
{ 4, "Over 20" }
});
正如您所看到的,我正在传递一个属性,然后“解开”它以获取源代码。我想做同样的事情,但要用上面描述的更简单的设置。
发布于 2018-06-26 20:58:40
基本上,您要做的是将参数声明为与您希望接受的方法签名相匹配的Func
,然后将其包装在Expression
中,这样编译器将为您提供表达式树,而不是实际的委托。然后,您可以遍历表达式树以查找可从中获取方法名称的MethodCallExpression
。(顺便说一句,除了属性之外,您提供的链接中的示例代码也可以处理方法调用,就像您希望的那样)
此
DoSomethingWithMethod
应具有什么签名
这取决于您想要作为参数的方法的签名。如果某些方法看起来像这样:
public MyReturnType SomeMethod(MyParameterType parameter) {}
然后,DoSomethingWithMethod
签名将如下所示:
public void DoSomethingWithMethod(Expression<Func<MyParameterType,MyReturnType>> methodExpression) {}
您还可以将其设为泛型,以防您希望接受具有稍微不同签名的方法(但是,如果您希望接受具有不同数量参数的方法,则必须使用重载,而且在这种情况下,C#编译器可能不会自动解析泛型类型参数,您必须显式指定它们)。
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression) {}
及其实际应该做的事情
我认为这个问题实际上是,如何从表达式树中获取字符串形式的方法名称?
有几种不同的方法可以做到这一点,这取决于你希望你的代码有多健壮。考虑到上面的方法签名允许传递比单个方法调用复杂得多的委托。例如:
DoSomethingWithMethod(t => t.SomeMethod().SomeOtherMethod(5) + AnotherThing(t));
如果你搜索上面生成的表达式树,你会发现相当多的方法调用,而不只是一个。如果只想强制使传递的参数是单个方法调用,那么尝试将表达式Body
属性强制转换为MethodCallExpression
可能会更简单
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression)
{
if (methodExpression.Body is MethodCallExpression methodCall)
{
var methodName = methodCall.Method.Name;
//...
}
}
另一种选择是使用访问者模式,如果你有一个更复杂的场景,比如当有多个方法时,你想检索所有方法名称的列表,或者支持属性或方法调用的混合,等等,这是很有帮助的。
对于此选项,请创建一个继承ExpressionVisitor
并覆盖基类中适当方法的类,并将结果存储在某个位置。下面是一个例子:
class MyVisitor : ExpressionVisitor
{
public List<string> Names { get; } = new List<string>();
protected override Expression VisitMember(MemberExpression node)
{
if(node.Member.MemberType == MemberTypes.Method)
{
Names.Add(node.Member.Name);
}
return base.VisitMember(node);
}
}
你可以这样称呼它:
var visitor = new MyVisitor();
visitor.Visit(methodExpression.Body);
var methodName = visitor.Names[0];
//...
最后,要调用它,您将不能使用缩短的“方法组”模式调用DoSomethingWithMethod
,因为C#编译器不能自动将方法组转换为表达式树(但是它可以自动将其转换为常规委托,这是您习惯的表示法)。
所以你不能这样做:
DoSomethingWithMethod(SomeMethod);
相反,它必须看起来像一个lambda表达式:
DoSomethingWithMethod(t => SomeMethod(t));
或者如果没有参数:
DoSomethingWithMethod(() => SomeMethod());
发布于 2018-06-26 07:37:56
您可以使用[CallerMemberName]
来获取调用方法的名称。
public void DoProcessing()
{
TraceMessage("Something happened.");
}
public void TraceMessage(string message,
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
System.Diagnostics.Trace.WriteLine("message: " + message);
System.Diagnostics.Trace.WriteLine("member name: " + memberName);
System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);
System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);
}
在上面的示例中,memberName
参数将被赋值为DoProcessing
。
样本输出
消息:发生了一些事情。
成员名称: DoProcessing
源文件路径: C:\Users\user\AppData\Local\Temp\LINQPad5_osjizlla\query_gzfqkl.cs
源码行号: 37
https://stackoverflow.com/questions/51033004
复制相似问题