Code Endeavor's Blog

Wrapping Our Results With Generics

Its an age old problem. You have a method that needs to report whether it executed successfully and if it did, you need to return the result.

int? SolveProblem(int x, int y)
{
  var errors = new List<string>();
  if (x < 0)
    errors.Add("x cannot be less than zero");
  if (y < 0)
    errors.Add("y cannot be less than zero");
  if (errors.length == 0)
    return x + y;
  else
    //now what?
}

Some developers will choose to return null / Nothing here to represent a failure. However, this is not really useful to the caller as they will not know why. Others will choose to throw an exception, which is a little better, but exceptions have overhead and really just push the same problem up the stack. If the calling method needs to return a list of errors to its caller the exception will need to be parsed to re-constitute the messages.

Another approach is to use an output / ByRef parameter (which is what Microsoft does with things like int.TryParse)

int? SolveProblem(int x, int y, out List<string> errors)

This is the best option yet, but requires the caller to define a variable to hold the errors prior to calling, which is ok, but not ideal.

List<string> errors;  //annoying
var answer = SolveProblem(x, y, out errors);

Of course we could create a "complex object" holding both the errors collection and the answer.

public class MyAnswer
{
  public MyAnswer()
  {
    Errors = new List<string>();
  }
    public List<string> Errors { get; set; }
  public int? Data { get; set; }
  public bool Success { get { return Errors.Count == 0; } }
}

This does produce a better experience for the caller, however, it is really annoying for the method developer since it would require a new class for each type of "answer" returned. That is, unless you decide to be a little creative with generics.

public class MyAnswer<T>
{
  public MyAnswer()
  {
    Errors = new List<string>();
  }
  public List<string> Errors { get; set; }
  public T Data { get; set; }
  public bool Success { get { return Errors.Count == 0; } }
}

Now we can use this class in all our methods, without exceptions, output parameters, or one-off classes.

MyAnswer<int?> SolveProblem(int x, int y)
{
  var answer = new MyAnswer<int?>();
  if (x < 0)
    answer.Errors.Add("x cannot be less than zero");
  if (y < 0)
    answer.Errors.Add("y cannot be less than zero");
  if (answer.Success)
    answer.Data = x + y;
  return answer;
}