【问题标题】:Does getting current class and method name reduce performance?获取当前类和方法名会降低性能吗?
【发布时间】:2015-08-25 08:18:28
【问题描述】:

我有一个日志记录类,它创建了一个 log4net 实例,我可以从代码中的任何位置调用它。我还有一种方法可以找出调用者的类和方法名。这是我的方法:

private static string CurrentMethod()
{
    StackTrace stackTrace = new StackTrace();
    MethodBase method = stackTrace.GetFrame(2).GetMethod();
    string cn = method.ReflectedType.Name;
    string mn = method.Name;

    string output = "[" + cn + "." + mn + "]";
    return output;
}

我发现了这些文章:link1Link2Link3Link4 等等,但没有一篇讨论效率和性能。 现在我有两个问题: 1-我可以在一个大项目中使用这种方法吗(一秒钟大约 1000 个请求)?我的意思是这对项目的效率和性能有多大影响? 2- 上面的方法有更好的写法吗?

这也是我的日志类的一部分。我想它会有所帮助:

public static class Tools_Log
{
    private static ILog logger;

    public static ILog GetLogger()
    {
        return logger ?? (logger = CreateLogger());
    }
    private static ILog CreateLogger()
    {
        //Some log4net initialization
        return LogManager.GetLogger("WebService");
    }

    private static string CurrentMethod()
    {
        StackTrace stackTrace = new StackTrace();
        MethodBase method = stackTrace.GetFrame(2).GetMethod();
        string cn = method.ReflectedType.Name;
        string mn = method.Name;

        string output = "[" + cn + "." + mn + "]";
        return output;
    }

    public static string MessageForLogFile(string message, string exeption, double time)
    {
        string currentMethod;
        string executiontime;
        string consumer;
        string body;
        string output;

        currentMethod = CurrentMethod();
        executiontime = ExecutionTime(time);
        body = BodyForLog(message, exeption);
        consumer = Consumer();

        output = OutPut(currentMethod, executiontime, consumer, body);
        return output;
    }
}

我这样调用日志类:

Tools_Log.GetLogger().Info(Tools_Log.MessageForLogFile("some text", "some text", execution time));

谢谢。

【问题讨论】:

  • 如答案中所述,新的 CallerMemberNameAttribute 是救生员。如果由于某种原因您卡在较旧的 .net 框架版本中,则获取特定的堆栈帧应该比获取整个堆栈跟踪具有更好的性能,尤其是对于较大的堆栈(例如 new StackFrame(2,false).GetMethod();
  • 您可能还需要考虑编译器可能会优化/内联您的某些方法调用,并且您看到的堆栈跟踪或CallerMemberName 可能与您在调试时看到的不匹配。

标签: c#


【解决方案1】:

是的,反思总是一个较慢的过程。如果您想要方法的名称,您可以使用CallerMemberNameAttribute 轻松获得。添加一个具有空默认值的字符串参数,并将该属性应用于它,编译器将为您发送名称。

此外,如果您正在寻找快速日志记录,请查看 ETW 而不是 Log4Net。

这里:https://msdn.microsoft.com/en-us/library/windows/desktop/bb968803(v=vs.85).aspx

这里:http://blogs.msdn.com/b/vancem/archive/2012/07/09/logging-your-own-etw-events-in-c-system-diagnostics-tracing-eventsource.aspx

编辑:
至于您在 cmets 中的问题,不幸的是,您至少无法直接获得方法 2 级别的名称。 CallerMemberName 基本上只是语法糖,它告诉编译器获取调用成员的名称,并将其放在参数中,因此它在单个级别上工作。但是,由于它依赖于默认参数来执行此操作,因此如果您手动发送参数,CallerMemberName 功能将不适用,因此您可以执行以下操作:

public void UserMethod()
{
    IntermediaryMethod();
}

public void IntermediaryMethod([CallerMemberName] caller = "")
{
    LogMethod(caller)
}

public void LogMethod([CallerMemberName] caller = "")
{
    // Log...
}

如果您自己传递一个值,它不会被覆盖,因此执行此类操作将允许您从 2 层以上获取调用者的名称,同时保留单层的功能。

【讨论】:

  • 另请注意,在 C# 6 中,您可以使用 nameof 表达式,例如 static void MyMethod() { string n = nameof(MyMethod); /* ... */ }。如果 MyMethod 稍后重命名,这是安全的,因为在编译时会检查标识符 MyMethod 是否存在(并且拼写正确等)。
  • 谢谢你亲爱的@CKII。但是如何找出来电者的姓名呢?我的意思是我使用stackTrace.GetFrame(2).GetMethod(); 找出来电者姓名,因为它在我的日志类中。谢谢。
  • @Dan 将参数添加到您的 MessageForLogFile 方法中。然后你会得到任何调用 that 方法的人的名字——这就是你要找的人。
  • 谢谢。但有时我需要再回去一次。 stackTrace 就这么简单。我可以用CallerMemberNameAttribute吗?
【解决方案2】:

必须测量性能,这是使用分析器完成的。

顺便说一句,您可以使用 .NET 4.5 中引入的 caller information 属性,而不是使用 StackTrace 类。

虽然[CallerMemberName] 属性不提供类名,但[CallerFilePath] 提供了定义调用者成员的源代码文件的完整路径(它可能更好地用于调试/记录目的,因为您知道在哪里调查可能的错误或错误)。

【讨论】:

  • 需要注意的一点是[CallerFilePath]在编译时提供了类的完整文件路径:“这是编译时的文件路径。”因此,如果您将代码移动到另一台 PC,它将始终具有相同的路径,如果该路径是您的桌面,那么您的 PC 用户名将显示在路径中(对于 Windows 用户)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-12
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 1970-01-01
  • 2020-10-22
  • 1970-01-01
相关资源
最近更新 更多