Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
February 20, 2022 10:20 pm GMT

Hacking IDisposable: The Stopwatch Example

Provides a mechanism for releasing unmanaged resources.

This is the first sentence you can read about IDisposable interface in the Microsoft documentation - and it definitely should be the primary usage of it. However, the C# language is cool enough to have a special using statement that comes along when using objects that implement IDisposable. There is something elegant and compact about this language structure, which makes me really like it - that's why I use it even for places that it was not designed for. Like, for example, wrapping up the Stopwatch.

Stopwatch

The Stopwatch class was introduced in .NET Framework 2.0 and since then became a de facto standard for measuring code execution time. If you were ever interested in checking how long does it takes to run a specific part of your program, you've probably stumbled into it.

The basic usage pattern of a Stopwatch class is as follows:

var stopwatch = new Stopwatch();stopwatch.Start();// Code which execution time we want to measurestopwatch.Stop();Console.WriteLine(stopwatch.Elapsed);

There are a couple of caveats - static method Stopwatch.StartNew() can be used as a shortcut for creating Stopwatch instance and starting it at the same time. Also, the Elapsed property can be accessed without actually stopping the stopwatch first - it will keep on counting time.

So the structure is already there - we start the stopwatch, we execute the code we want to measure, we stop the stopwatch. Whenever and wherever you do it, the structure always looks similar to this. And since it's as simple as wrapping the "proper" code you want to measure with some init and teardown actions, the using statement might be a good fit.

Disposable stopwatch

So lets start with defining a class implementing IDisposable.

public class Timer : IDisposable{    private readonly Stopwatch _stopwatch;    public Timer()    {        _stopwatch = Stopwatch.StartNew();    }    public void Dispose()    {        _stopwatch.Stop();        Console.WriteLine(_stopwatch.Elapsed);    }}// Usage exampleusing (new Timer()){    // Code which execution time we want to measure}

This is functionally identical to the previous example and for me looks better already, however there are a couple of things we can do to make it more useful.

First of all, writing to the console output is not necessarily the only thing we want to do with the measured time span. Sometimes it might be, but sometimes we'd rather write it to file or to an external system, eventually ending up in some kind of monitoring platform.

So we could parametrize the Timer and allow its clients to decide what to do with the measured execution time.

public class Timer : IDisposable{    private readonly Action<TimeSpan> _afterMeasuredAction;    private readonly Stopwatch _stopwatch;    public Timer(Action<TimeSpan> afterMeasuredAction)    {        _afterMeasuredAction = afterMeasuredAction ?? (_ => { });        _stopwatch = Stopwatch.StartNew();    }    public void Dispose()    {        _stopwatch.Stop();        _afterMeasuredAction.Invoke(_stopwatch.Elapsed);    }}// Usage exampleusing (new Timer(measuredTime => Console.WriteLine(measuredTime))){    // Code which execution time we want to measure}

There's something I still don't like about this usage pattern. Maybe because we're newing up an object here that we don't really care about - not even assigning it to any variable. Effectively there's nothing wrong about it, cause what we really want is to create it and dispose automatically at the end of the using block. But if we don't need to do any other thing with this object - why do we need to know its type? Well, we don't - knowing that it implements IDisposable should be more than enough for us. So instead of newing up an instance of a specific type, I'd rather hide it with some method and directly return just an IDisposable.

public class Measurement{    public static IDisposable Run(        Action<TimeSpan> afterMeasuredAction = null) =>            new Timer(afterMeasuredAction);    private class Timer : IDisposable    {        private readonly Action<TimeSpan> _afterMeasuredAction;        private readonly Stopwatch _stopwatch;        public Timer(Action<TimeSpan> afterMeasuredAction)        {            _afterMeasuredAction = afterMeasuredAction ?? (_ => { });            _stopwatch = Stopwatch.StartNew();        }        public void Dispose()        {            _stopwatch.Stop();            _afterMeasuredAction.Invoke(_stopwatch.Elapsed);        }    }}// Usage exampleusing (Measurement.Run(measuredTime => Console.WriteLine(measuredTime)){    // Code which execution time we want to measure}

One last thing I'd like to enhance a bit. Since Measurement is a very generic purpose class - probably for use in many places, as we usually want to measure execution time of multiple methods or algorithms within our code base - we'd normally define this action after measure (like Console.WriteLine) many times. To avoid that we can create a specific class that uses the generic one.

public class LogMeasurement{    private readonly ILogger<LogMeasurement> _logger;    public LogMeasurement(ILogger<LogMeasurement> logger)    {        _logger = logger;    }    public IDisposable Run() => Measurement.Run(        timeSpan => _logger.LogTrace($"Measured time: {timeSpan}"));}// Usage exampleusing (_logMeasurement.Run()){    // Code which execution time we want to measure}

Note that this time we have an instance Run() method instead of a static one as previously. The reason is that I'd probably want to inject this class in all the places I'd like to use it, so it's ready for usage in a dependency injection scenario. That's also why it has a constructor with its own dependencies injected. In other words we now have one class to gather everything needed to do whatever we want to do in our system with measured code execution time.

And if you sometimes want to do something else with it - you just define another similar class, which uses Measurement.Run with a different action, and inject it instead of the LogMeasurement one!

Post Scriptum

This blog post is mostly about answering the "How to measure code execution time?" question. One thing I haven't and won't dig too deep into is an answer to "Where to measure it?" question. But just briefly - I do think that any code not related strictly to the domain of the class or method just adds noise and distraction. So adding some logging, monitoring or execution time measuring logic in-place with the domain code is not necessarily the best idea. I'd rather extract it to some kind of decorator class instead.

Also, sorry for the clickbaitish title - the provided example is not really hacking the interface in any way. But it can be considered a usage outside of the definition mentioned at the very beginning, so in this sense what we're really doing is exploring the undocumented ways of using it. Sounds hacky enough for me!

Just code

Hacking IDisposable: The Stopwatch Example on GitHub + Tests


Original Link: https://dev.to/jakubkwa/hacking-idisposable-the-stopwatch-example-1ja0

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To