Click here to Skip to main content
13,258,269 members (42,026 online)
Click here to Skip to main content
Add your own
alternative version

Stats

5K views
8 bookmarked
Posted 13 Aug 2017

SimpleHelpers are here to save the day!

, 13 Aug 2017
Rate this:
Please Sign up or sign in to vote.
Helper methods should be simple, and these SimpleHelpers make your code simpler, too!

Introduction

Long ago, in a data center far away, there was a programmer who was tired of writing the same code over and over and over. He sipped his coffee, walked to the printer room to get the 400 pages of his source code, and then went back to his deck of punch cards and had an epiphany: What if I only write a single punch card to be reused from my other punch cards? I could save 40 punch cards (and about 50 pages of source code)!

The Helper Method was born.

OK, so maybe thats just a bit of a dramatization of what happened, but who knows? In an infinite universe Star Wars is a documentary somewhere. The point is that boilerplate code has been around a long time, and it has been a problem for just as long. Today we have Resharper and IDEs that magically tell you how to write better code. Instead of you figuring out the best way to write that block of code, you tell the IDE the gist of what you want and it will offer to reformat it for you.

But what about when the magic-black-box cant see the patterns in your boilerplate and wont help you. Do you get out your moldy box of reusable punch cards? No! You download GPS.SimpleHelpers from nuget.org and make your life a lot easier.

Background

GPS.SimpleHelpers is a collection of classes that speed you through your development of some truly gnarly use cases that theres just no good way to handle until lambdas came along and changed the world (for the better) forever.

Simple Helpers currently has three problem domains covered: code timing, exception handling, and data marshalling.

Using the Code

First, you can get the full source code from Github. If you dont care about the full source code and just want to start using GPS.SimpleHelpers, you can retrieve it from Nuget.org with your favorite package manager (I like paket) by installing package GPS.SimpleHelpers.

Stopwatch

First, lets talk about some of the most repetitive boilerplate code in your projects. Metrics gathering with the Stopwatch class. Weve all written this code before:

var sw = new Stopwatch();

sw.Start();

// Do something expensive

System.Diagnostics.Debug.WriteLine(sw.Elapsed);
sw.Stop();

Thats four lines of code too much! What if we could do all of that in one line of code?

StopwatchHelpers.TimeAction(() => /* Do something expensive */,
  e => Debug.WriteLine);

Now weve encapsulated the pattern and we dont have to keep writing the same boilerplate over and over.

Lets look at how we did that.

public static void TimeAction(Action action, Action<long> onFinish)
{
  var sw = new System.Diagnostics.Stopwatch();

  sw.Start();

  action();

  onFinish(sw.ElapsedMilliseconds);

  sw.Stop();
}

Its really the same code, we just take two Actions as input to be executed. One great thing about this technique is that you can encapsulate your logic for handling the elapsed time with something like this.

public void LogTimer(long elapsed)
{
  if(elapsed >= 1000) _log.DEBUG($RED ALERT! {elapsed} ms passed!);

  if(elapsed >= 500) _log.INFO(${elapsed} ms passed.);
}

// ..
StopwatchHelpers.TimeAction(MyLongAction, LogTimer);
// ..

Now you can inject complex handling logic into your timings and not pollute your methods with non-related logic.

TimeAction has the following overloads. They all work the same way:

public static TReturn TimeAction<TData, TReturn>(
 TData value, Func<TData, TReturn> func, Action<long> onFinish)
public static void TimeAction<TData>(
     TData value, Action<TData> action, Action<long> onFinish)
public static void TimeAction(Action action, Action<long> onFinish)
public static long TimeAction(Action action)
public static long TimeAction<TData>(TData value, Action<TData> action)

Try-Catch-Finally

Another common boilerplate activity is try-catch-finally blocks.

var dbContext = new DbContext();
var exceptions = new ConcurrentQueue<Exception>();

try
{
  dbContext.Table.ForEach(rec =>
  {
  try
    {
      SomeActionsWithDbContext(dbContext);
    }
    catch (Exception ex)
    {
      ex.LogSomewhere(your message);
      enqueue(exceptions, ex);
    }
  }
}
finally
{
  someCleanupLogic(exceptions);
  dbContext.Dispose();
}

ProcessExceptions(exceptions);

Thats a lot of code! Lets try that again with SafeCallHelpers.

var dbContext = new DbContext();
var exceptions = new ConcurrentQueue();

SafeCallHelpers.TryCall(() =>
  { enqueue(exceptions, SafeCallHelpers.TryCall(() =>
    { someActionsWithDbContext(dbContext); });
  }
  ,() =>
  { 
    someCleanupLogic(exceptions);
    dbContext.Dispose();
  });

ProcessExceptions(exceptions);

Now, you can remove the ugly boilerplate templates. Using this pattern has a couple of advantages:

  • 1. Forces you to implement consistent Exception handling.
  • 2. Forces you to implement consistent finally blocks. Its very easy to forget to utilize the finally block to dispose of your IDisposable objects.

This helper may not be your style. Thats ok, unlike the StopwatchHelpers, this is here as a personal preference and dislike of deeply nested try-catch-finally blocks, which I find hard to read, especially as the nesting gets deeper.

Data Marshalling

Concurrent processing causes one to start to understand the value of coding for asynchronicity (yup, I think I just made up a word). To that end, we have constructs like the ConcurrentDictionary which uses the AddOrUpdate and GetOrCreate methods. GetOrCreate is really interesting because it forces a return value to come back from the ConcurrentDictionary, so you never have to code for a null return.

This is very powerful from a semantic aspect. Removing null reference checks when the result is guaranteed to be an instance of the requested object allows for much cleaner code. Microsoft helped a lot with the null propagation operator, but you still must account for nulls from the propagation.

Take this code for example.

var dictionary = new ConcurrentDictionary<string, Record>();
using(var dbContext = new DbContext())
{
  Parallel.ForEach(dbContext.Table.Where(rec => rec.Field == someKey), rec =>
  {
    dictionary.AddOrUpdate(someKey, rec);
  }
}
var value = dictionary.GetOrAdd(someSpecificKey,
  () => { return new Record { RequiredField = Unset }; });

Microsofts parallel processing additions with .Net 4.0 make thread-safe code a breeze. But what if we want to do this same thing with sequential code? You could still use the ConcurrentDictionary, but it has a lot of overhead compared to the standard Dictionary object.

var dictionary = new Dictionary<string, Record>();
using(var dbContext = new DbContext())
{
  foreach(var rec in dbContext.Table.Where(rec => rec.Field == someKey))
  {
    dictionary.Add(someKey, rec);
  }
}
var value = dictionary[someKey];
if(value == null) value = new Record { RequiredField = Unset };

Thats reasonable, right? Except its really, really, easy to forget the null check and then you wind up with the infamous null reference exceptions. Lets look at it with the Marshaller in place.

//  The Loop
var value = SafeMarshalling.GetOrBuild(
  () => { return dictionary[someKey]; },
  () => { return new Record { RequiredField = Unset }; });

Yay! No null reference checking! You always get a value.

Putting it all together

Ok, so lets make one big ball of spaghetti to prove this all works together. 😊

var exceptions = new List<Exception>();
var dictionary = new Dictionary<string, string>();

AddException(exceptions, (SafeCallHelpers.TryCall(() =>
{
  StopwatchHelper(() =>
  {
    dictionary.Add(someKey, SafeMarshalling.GetOrBuild(
      () => SomeExpensiveGetter,
      () => SomeExpensiveBuilder));
  }, elapsed => LogElapsedTime);
}, CleanupMethod));

From these nine lines of code weve done the following:

  • Used an expensive getter or expensive builder to create an object.
  • Add that object to a Dictionary.
  • Caught any Exceptions.
  • Disposed any IDisposable objects.

Lets look at the same code written traditionally.

var exceptions = new List<Exception>();
var dictionary = new Dictionary<string, string>();
try
{
  var sw = new Stopwatch();
  sw.Start();
  var someValue = SomeExpensiveGetter();
  if(someValue == null) someValue = SomeExpensiveBuilder();
  dictionary.Add(someKey, someValue);
  LogElapsedTime(sw.Elapsed);
  sw.Stop();
}
catch(Exception ex)
{
  AddException(exceptions, ex);
}
finally
{
  CleanupMethod();
}

Thats twice as much code! And I personally dont find it any cleaner.

Conclusion

Its easy to dismiss helper methods as just crutches that real programmers dont need. I dont know if Im a real programmer, but Ive been writing code since I was 13, and Ive had a pretty nice career applying repeatable code patterns to complex problems. Dont dismiss the power of the helper method until youve tried it for yourself!

Interesting Facts

I wrote my first helper method (subroutine) in CBM BASIC 2.0 on a Commodore VIC 20 in 1984.

Revisions

Version 1.0.0

  • Initial Version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Sharp Ninja
United States United States
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionReally nice, but... Pin
Klaus Luedenscheidt13-Aug-17 20:41
memberKlaus Luedenscheidt13-Aug-17 20:41 
AnswerRe: Really nice, but... Pin
Sharp Ninja14-Aug-17 1:12
memberSharp Ninja14-Aug-17 1:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.171114.1 | Last Updated 13 Aug 2017
Article Copyright 2017 by Sharp Ninja
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid