你会如何重构这个臭臭的代码? (记录,复制和粘贴,.Net 3.5)

时间:2009-05-08 16:29:28

标签: c# .net .net-3.5 refactoring

我有这样的代码:

Logger logger = new Logger();
System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
logger.LogInformation("Calling SomeObject.SomeMethod at " + DateTime.Now.ToString());
stopWatch.Start();
// This is the method I'm interested in.
SomeResponse response = someObject.SomeMethod(someParam);
stopWatch.Stop();
logger.LogInformation("SomeObject.SomeMethod returned at " + DateTime.Now.ToString());
logger.LogInformation("SomeObject.SomeMethod took " + stopWatch.ElapsedMilliseconds + " milliseconds.");

我需要围绕更多对象及其方法包装类似的代码来进行一些性能分析。我不允许使用第三方插件或软件等。

我真的不想在所有这些方法调用这个所有这些日志代码中编写相同的代码。您如何重构这一点以消除我的一些编码工作?

如果我不是很清楚,请在评论中提问,我会尽力澄清。

感谢您的帮助!!

9 个答案:

答案 0 :(得分:19)

您可以重构代码以接受方法指针实例(又名System.Action)。

public void CallWithLogTiming (Action theAction)
{
  Logger logger = new Logger();
  System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();
  logger.LogInformation("Calling SomeObject.SomeMethod at " + DateTime.Now.ToString());
  stopWatch.Start();

// This is the method I'm interested in.
  theAction();

  stopWatch.Stop();
  logger.LogInformation("SomeObject.SomeMethod returned at " + DateTime.Now.ToString());
  logger.LogInformation("SomeObject.SomeMethod took " + stopWatch.ElapsedMilliseconds + " milliseconds.");
}

然后你可以通过创建一个lambda表达式来调用它。由于myResponse是一个捕获变量,因此在运行此Action时将填充该变量,myResponse将在稍后的范围内使用。

SomeResponse myResponse = null;
CallWithLogTiming( () => myResponse = someObject.SomeMethod(someParam) );

答案 1 :(得分:6)

为简单起见,您可以使用泛型,就像这样(在我的脑海中):

public T MyLogMethod<T,S>(Func<S, T> someFunction, S someParameter) {}

Func(S,T)其中S是方法的参数类型,T是返回类型。

答案 2 :(得分:5)

我想我实现了一个可以像这样使用的计时器类:

void test()
{
  foo();
  //timer for the following statements
  using (new MyTimer("Some method"))
  {
    bar();
  }
  baz();
}

MyTimer类的实现如下:

  • 包含秒表实例和标识计时器的消息字符串
  • 构造函数启动秒表并记住消息字符串
  • Dispose方法停止秒表,并记录消息字符串和已用完的时间

答案 3 :(得分:2)

绝对是AOP的候选人。我们使用PostSharp来做这类事情。 http://www.postsharp.org/

答案 4 :(得分:2)

总有PostSharp库允许您执行面向方面的代码。它允许您将日志记录和秒表作为属性进行操作,这很酷。它将在您的方法中注入前后代码作为后编译步骤。

此外,您可能会考虑使用这样的一些计时器/记录器静态方法来包装您想要时间/日志的代码:

Timer.LogExecutionTime("SomeObject.SomeMethod", () =>
{
    Logger.LogBeforeAndAfter("SomeObject.SomeMethod", () =>
    {
        SomeResponse response = someObject.SomeMethod(someParam);
    }
});

答案 5 :(得分:2)

你可以在Logger上使用一些简单的扩展方法使语法变得更清晰,这不需要你已经拥有的其他程序集,你可以立即插入它。如果您要在整个代码中多次执行此操作,那么它是可重用的。

public static class LoggerExtentions
{
    public static void StartTimerLogInformation(this Logger logger, Stopwatch stopWatch, string method)
    {
        stopWatch.Reset();
        stopWatch.Start();
        logger.LogInformation(string.Format("Calling {0} at {1}", method, DateTime.Now.ToString()));
    }        

    public static void StopTimerLogInformation(this Logger logger, Stopwatch stopWatch, string method)
    {
        stopWatch.Stop();
        logger.LogInformation(string.Format("{0} returned at {1}", method, DateTime.Now.ToString()));
        logger.LogInformation(string.Format("{0} took {1} milliseconds", method, stopWatch.ElapsedMilliseconds));
        stopWatch.Reset();
    }
}

然后您可以使用此代码替换原始帖子中的代码

Logger logger = new Logger();
Stopwatch stopWatch = new Stopwatch();
logger.StartTimerLogInformation(stopWatch, "SomeObject.SomeMethod");
SomeResponse response = someObject.SomeMethod(someParam);
logger.StopTimerLogInformation(stopWatch, "SomeObject.SomeMethod");

答案 6 :(得分:1)

如果你写一个这样的类(我正在使用Java;可能有一些东西不能完全翻译):

public class myProfiler {
    final String method;
    final Logger logger = new Logger();
    final System.Diagnostics.Stopwatch stopWatch = new Stopwatch();
    public myProfiler(method) {this.method = method};
    public void Start() {
        logger.LogInformation("Calling " + method + " at " + DateTime.Now.ToString());
        stopWatch.Start();      
    }
    public void Stop() {
        stopWatch.Stop();
        logger.LogInformation(method + " returned at " + DateTime.Now.ToString());
        logger.LogInformation(method + " took " + stopWatch.ElapsedMilliseconds + " milliseconds.");
    }
}

然后,您已将每种方法所需的代码缩减为

myProfiler profiler = new myProfiler("SomeObject.SomeMethod");
profiler.Start();
...
profiler.Stop();

答案 7 :(得分:0)

如何在所有对象上继承计时器类?

答案 8 :(得分:0)

在这些情况下我喜欢做的是使用一次性模式来实现我的计时器;如果出现错误,您将得到正确的清理和记录:

    public class Timer : IDisposable
    {
        Logger logger = new Logger();
        Stopwatch stopWatch = new Stopwatch();

        public Timer()
        {
            calledFunc = CalledFunc;
            logger.LogInformation("Calling SomeObject.SomeMethod at " +
                DateTime.Now.ToString());
            stopWatch.Start();
        }

        // Dispose() calls Dispose(true)
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        // NOTE: Leave out the finalizer altogether if this class doesn't 
        // own unmanaged resources itself, but leave the other methods
        // exactly as they are. 
        ~Timer()
        {
            // Finalizer calls Dispose(false)
            Dispose(false);
        }
        // The bulk of the clean-up code is implemented in Dispose(bool)
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                // free managed resources
                stopWatch.Stop();
                logger.LogInformation("SomeObject.SomeMethod returned at " +
                    DateTime.Now.ToString());
                logger.LogInformation("SomeObject.SomeMethod took " +
                    stopWatch.ElapsedMilliseconds + " milliseconds.");
            }
            // free native resources if there are any.
        }
    }

然后你使用这样的计时器:

using (var t = new Timer())
{
   // your code
}

当然,您可以将args传递给计时器(方法名称,记录器等),以便自定义设置和处置中发生的事情

相关问题