将方法名作为参数传递

9
private void Method1()
{
    //Do something
    Log("Something","Method1");
}

private void Method2()
{
    //Do something
    Log("Something","Method2");
}

private void Log(string message, string method)
{
    //Write to a log file
    Trace.TraceInformation(message + "happened at " + method);
}

我有几个类似于上面的Method1和Method2的方法,我希望能够以某种方式将方法名称作为参数传递,而不是手动编辑代码。
这是否可能?

3
有一个新的属性(我认为是在4.5版本中,并需要最新的编译器)叫做CallerMemberNameAttribute,它可以以字符串字面量的形式在编译时完成这项工作。或者,您可以使用Linq表达式并查询表达式本身的名称,这经常用于属性和INotifyPropertyChanged等情况。 - Adam Houldsworth
1
你可以使用反射来代替传递方法名称。 - Amit Bisht
1
请参考 https://dev59.com/JnE85IYBdhLWcg3wtVxT. - Uri Y
3个回答

21

从C# 5开始,使用调用者信息特性非常容易:

private void Method1()
{
    //Do something
    Log("Something");
}

private void Method2()
{
    //Do something
    Log("Something");
}

private void Log(string message, [CallerMemberName] string method = null)
{
    //Write to a log file
    Trace.TraceInformation(message + "happened at " + method);
}

就让我们来谈一下如何使它起作用:

  • 您必须使用C# 5(或更高版本)编译器,否则它不知道如何特殊处理属性
  • 属性必须存在于目标环境中。有以下几个选项:
    • 在.NET 4.5中,这些属性已经存在了
    • 对于.NET 4,您可以使用Microsoft.Bcl NuGet软件包
    • 对于早期版本的.NET,请将属性声明复制到您自己的代码中,并确保您使用相同的命名空间。当您以后升级到新版本的.NET时,您需要再次删除它。

非常好的回答,Jon。只是好奇:在进行“单元测试”时,这个属性会如何表现?我们可以模拟吗? - now he who must not be named.
1
@现在不得不被命名的人:这是一个编译时的事情——编译器注意到参数已经被装饰了属性,并且有默认值为 null,调用者没有提供参数,所以它使用成员名称。我不确定你说的是什么样的模拟,但你可以很容易地显式传递一个参数(例如 Log("message", "NotARealMethod"))。 - Jon Skeet
谢谢Jon。可选参数。很有道理。 :) - now he who must not be named.
如果在旧版本的.NET中但仍使用C# 5编译器,是否值得添加有关自己创建属性的详细信息? - Joe
1
@JoeStead:我会添加一个简短的注释,但不包括完整的细节。 - Jon Skeet

5

Jon Skeet的回答非常好。

然而,如果你不使用.NET 4.5,你可以尝试使用反射。但是你应该知道,只有在绝对必要的情况下才应该使用反射。不要为了使用而过度使用反射

回到问题上,你可以这样做:

 using System.Reflection; //include Reflection namespace

 Console.WriteLine(MethodBase.GetCurrentMethod().Name) //Get the method-name of the current method

在你的情况下,应该像下面这样:
private void Method1()
{
    //Do something
    Log("Something", System.Reflection.MethodBase.GetCurrentMethod().Name);
}

private void Method2()
{
    //Do something
    Log("Something", System.Reflection.MethodBase.GetCurrentMethod().Name);
}

private void Log(string message, string method)
{
    //Write to a log file
    Trace.TraceInformation(message + "happened at " + method);
}

编辑:

根据@Jon Skeet的下面评论,如果你想要类似于.Net 4.5的漂亮实现,可以查看Micrsoft.Bcl NUGET包。


我无法使用.NET 4.5或5,因此反射似乎是唯一的方法。 - gorkem
什么是意义?你可以在参数中直接放置方法名称,因为当你编写代码时,你实际上已经知道它的名称。这不是解决问题的方法,只是帮助更轻松地复制粘贴代码。 - Artyom Neustroev
1
@gorkem:不,这不是唯一的方法。如果您可以使用C# 5编译器,您可以自己添加属性。 - Jon Skeet
@JonSkeet:谢谢Jon。您的意思是我们可以在较低版本的.NET中推出自定义[CallerMemberName]吗?请给我一个示例。 - now he who must not be named.
1
@nowhewhomustnotbenamed:是的,你可以。在.NET 4中,只需使用Micrsoft.Bcl NuGet包;对于旧版本,只需从MSDN复制属性声明,并确保将其放在与真实属性相同的命名空间中。 - Jon Skeet
显示剩余2条评论

0

面向切面编程(AOP)通常可以实现这些任务。您可以查看PostSharp的免费版本,特别是Logging aspect在您的情况下非常有帮助。

然后您的代码看起来像这样:

[LogAspect("Something")]
void Method1(string name)
{

}

您可以在 .NET Framework 2.0 及以上版本中使用 PostSharp。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接