1. Daniel D'Agostino
  2. Dandago.Utilities

Wiki

Clone wiki

Dandago.Utilities / Home

Dandago.Utilities

This is a collection of simple utilities that I found myself creating over and over again.

It is distributed as the Dandago.Utiities NuGet package, which is currently in beta. Install it using the following command:

Install-Package Dandago.Utilities -Pre

Some examples are provided in the source code (and in this documentation).

IEnumerable extensions

string.IsNullOrEmpty() is an ubiquitous method because it makes a trivial but extremely common operation notably concise. Why can't we also use this on arrays, lists, and all kinds of collections too?

Of course you can. Just install Dandago.Utilities via NuGet, add using Dandago.Utilities.Extensions, and then use IsNullOrEmpty() on your IEnumerables as if they were strings.

            List<int> list1 = null;
            List<int> list2 = new List<int>();
            List<int> list3 = new List<int>() { 1 };

            Console.WriteLine("list1 null/empty: " + list1.IsNullOrEmpty()); // True
            Console.WriteLine("list2 null/empty: " + list2.IsNullOrEmpty()); // True
            Console.WriteLine("list3 null/empty: " + list3.IsNullOrEmpty()); // False

ScopedConsoleColour

Straight out of my Scope Bound Resource Management article comes the ScopedConsoleColour. It's great for when you want to write something to Console in a different colour and then switch back; you can use it with a regular using block to keep your code nice and tidy:

            Console.WriteLine("Normal Text!");

            using (var colour = new ScopedConsoleColour(ConsoleColor.Red))
                Console.WriteLine("Red Text!");

            Console.WriteLine("Normal Text Again!");

scopedconsolecolour.png

ScopedTimer

In the same Scope Bound Resource Management article, I had reused the same technique to neatly wrap logging code and benchmarking code elegantly in a using block, to mitigate littering business code with such cross-cutting concerns.

The ScopedTimer implementation provided in Dandago.Utilities is a combination of these: it writes a message at the start and end of a code block, and it writes the time taken to execute the code in between. Since the actual details of how this should work vary wildly between projects (more on this in a minute), this implementation is a lot more elaborate than what was originally presented in the article, to provide maximum flexibility.

Here's how you use it:

            var scopedTimerFactory = new ScopedTimerFactory<ConsoleScopedTimer, StopwatchTimeKeeper>();

            using (var timer = scopedTimerFactory.Create("Benchmark"))
            {
                Console.WriteLine("Doing stuff!");
                Thread.Sleep(100);
            }

scopedtimer1.png

There are two main things that can vary in Scoped Timers: the way you keep track of time, and the way you write out your start and end messages.

The classes that track time implement ITimeKeeper. There are two default implementations (and you can also write your own). DateDiffTimeKeeper simply keeps track of the start and end time, and calculates the elapsed time by simple subtraction. For simple benchmarking this usually does the job. But if you want more precision, use StopwatchTimeKeeper instead, which uses a System.Diagnostics.Stopwatch underneath.

The way you write start and end messages is extremely subject to personal taste. People want different text, different formatting, perhaps different colours, and even different output (console, logger, etc). You are given ConsoleScopedTimer out of the box, but you will probably want to create your own class deriving from BaseScopedTimer, which in turn implements IScopedTimer. This is simply a matter of implementing the WriteBegin() and WriteEnd() methods. Look at the implementation of ConsoleScopedTimer to get an idea.

Since classes are highly compact and implement interfaces, it is really easy to set this up with dependency injection so that you can pass your ScopedTimerFactory around:

            using (IKernel kernel = new StandardKernel())
            {
                kernel.Bind<IScopedTimerFactory>()
                      .To<ScopedTimerFactory<ConsoleScopedTimer, StopwatchTimeKeeper>>();

                var scopedTimer = kernel.Get<IScopedTimerFactory>();

                using (var timer = scopedTimerFactory.Create("Benchmark with DI"))
                {
                    Console.WriteLine("Doing stuff with DI!");
                    Thread.Sleep(100);
                }
            }

ScopedAsyncLock

Asynchronous code may require locking (and remember, you can't await in a lock block). This can be to prevent race conditions due to concurrent access to a critical section. However, it is also useful to force processing async void event handlers in sequence, especially when these are called by a third party library (e.g. RabbitMQ.Client) and they may thus be called in quick succession in a fire-and-forget manner, resulting in parallel and interleaved processing.

The conventional way of doing this is to use a semaphore. Dandago.Utilities hides away this detail and provides the means to do this cleanly in a using block:

        private static ScopedAsyncLockFactory factory = new ScopedAsyncLockFactory();

        static void RunAll()
        {
            Parallel.For(1, 1000, i => RunJob("Job " + i));
        }

        static async void RunJob(string str)
        {
            using (var scopedLock = await factory.CreateLockAsync())
            {
                Console.WriteLine("Start " + str);

                await Task.Delay(700);

                Console.WriteLine("End " + str);
            }
        }

The ScopedAyncLock allows only one thread at a time to access the block that it guards. The screenshot below shows the output along with some extra debugging information showing when the lock is taken and released:

scoped-async-lock.png

Updated