首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >获取传递的方法的名称,但不使用nameof

获取传递的方法的名称,但不使用nameof
EN

Stack Overflow用户
提问于 2018-06-26 07:29:03
回答 2查看 251关注 0票数 0

很容易声明一个方法,它将方法名作为字符串:

代码语言:javascript
复制
public void DoSomethingWithMethodName(string methodName)
{
    // Do something with the method name here.
}

并将其调用为:

代码语言:javascript
复制
DoSomethingWithMethodName(nameof(SomeClass.SomeMethod));

我想摆脱nameof并调用其他一些方法,如下所示:

代码语言:javascript
复制
DoSomethingWithMethod(SomeClass.SomeMethod);

然后能够获得与上面示例中相同的方法名称。使用一些Expression和/或Func魔法“感觉”可以做到这一点。问题是这个DoSomethingWithMethod应该有什么签名,它应该做什么!

====================================

这个问题似乎引起了很多困惑,答案假设了我没有问的问题。这是一个提示,我的目标是什么,但不能正确。这是针对一些不同的问题(我有一个解决方案)。我可以声明:

代码语言:javascript
复制
    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

然后使用:

代码语言:javascript
复制
    [Fact]
    public async Task CommercialExcelRaterService_ShouldNotThrowOnNumberOfStories() =>
        await CheckDictionary(() => EqNumberOfStories, 2);

其中,EqNumberOfStories是:

代码语言:javascript
复制
    public static LookupDictionary EqNumberOfStories { get; } = new LookupDictionary(new Dictionary<int, string>
    {
        { 1, "" },
        { 2, "1 to 8" },
        { 3, "9 to 20" },
        { 4, "Over 20" }
    });

正如您所看到的,我正在传递一个属性,然后“解开”它以获取源代码。我想做同样的事情,但要用上面描述的更简单的设置。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-06-26 20:58:40

基本上,您要做的是将参数声明为与您希望接受的方法签名相匹配的Func,然后将其包装在Expression中,这样编译器将为您提供表达式树,而不是实际的委托。然后,您可以遍历表达式树以查找可从中获取方法名称的MethodCallExpression。(顺便说一句,除了属性之外,您提供的链接中的示例代码也可以处理方法调用,就像您希望的那样)

DoSomethingWithMethod应具有什么签名

这取决于您想要作为参数的方法的签名。如果某些方法看起来像这样:

代码语言:javascript
复制
public MyReturnType SomeMethod(MyParameterType parameter) {}

然后,DoSomethingWithMethod签名将如下所示:

代码语言:javascript
复制
public void DoSomethingWithMethod(Expression<Func<MyParameterType,MyReturnType>> methodExpression) {}

您还可以将其设为泛型,以防您希望接受具有稍微不同签名的方法(但是,如果您希望接受具有不同数量参数的方法,则必须使用重载,而且在这种情况下,C#编译器可能不会自动解析泛型类型参数,您必须显式指定它们)。

代码语言:javascript
复制
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression) {}

及其实际应该做的事情

我认为这个问题实际上是,如何从表达式树中获取字符串形式的方法名称?

有几种不同的方法可以做到这一点,这取决于你希望你的代码有多健壮。考虑到上面的方法签名允许传递比单个方法调用复杂得多的委托。例如:

代码语言:javascript
复制
DoSomethingWithMethod(t => t.SomeMethod().SomeOtherMethod(5) + AnotherThing(t));

如果你搜索上面生成的表达式树,你会发现相当多的方法调用,而不只是一个。如果只想强制使传递的参数是单个方法调用,那么尝试将表达式Body属性强制转换为MethodCallExpression可能会更简单

代码语言:javascript
复制
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression)
{
    if (methodExpression.Body is MethodCallExpression methodCall)
    {
        var methodName = methodCall.Method.Name;
        //...
    }
}

另一种选择是使用访问者模式,如果你有一个更复杂的场景,比如当有多个方法时,你想检索所有方法名称的列表,或者支持属性或方法调用的混合,等等,这是很有帮助的。

对于此选项,请创建一个继承ExpressionVisitor并覆盖基类中适当方法的类,并将结果存储在某个位置。下面是一个例子:

代码语言:javascript
复制
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);
    }
}

你可以这样称呼它:

代码语言:javascript
复制
var visitor = new MyVisitor();
visitor.Visit(methodExpression.Body);
var methodName = visitor.Names[0];
//...

最后,要调用它,您将不能使用缩短的“方法组”模式调用DoSomethingWithMethod,因为C#编译器不能自动将方法组转换为表达式树(但是它可以自动将其转换为常规委托,这是您习惯的表示法)。

所以你不能这样做:

代码语言:javascript
复制
DoSomethingWithMethod(SomeMethod);

相反,它必须看起来像一个lambda表达式:

代码语言:javascript
复制
DoSomethingWithMethod(t => SomeMethod(t));

或者如果没有参数:

代码语言:javascript
复制
DoSomethingWithMethod(() => SomeMethod());
票数 2
EN

Stack Overflow用户

发布于 2018-06-26 07:37:56

您可以使用[CallerMemberName]来获取调用方法的名称。

代码语言:javascript
复制
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://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute(v=vs.110).aspx

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51033004

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档