Thursday, January 10, 2013

C# - bringing implicit-defined variables out of scope

I have a habit of using implicit-typed declarations for everything in my code, because I think it makes things clean, simple, and easier to maintain.  It also has the side effect of causing all your variable declarations to be short and line up because they all start with "var".

If you're like me and use implicit typing, you may have encountered the issue where an implicitly-typed variable needs to be brought out of an inner scope into an outer scope.  For example, suppose you are requesting some data from a database and you want to handle any Exceptions.  Naturally, you wrap the data in a try...catch call:

try {
  var theData = GetData();
} catch (Exception ex) {
  // handle exception
}

// do something with theData

Now suppose you want to use theData later in the code.  How do you reference it?  You have to define theData outside of the try..catch, and initialize it to a null value.  Since you can't implicit-type a value of null, this requires you to explicitly define the type for the variable.  So much for implicit-typed declarations!

There is a clever workaround to this situation by making use of generics and lambda expressions. The compiler is usually able to determine the proper type of any lambda expression if it's a simple return value and method call. Supposing that GetData returns a type of IQueryable<Company>, say, the following lambda expression clearly has a type of Func<IQueryable<Company>>:

() => {
  try {
    return GetData();
  } catch (Exception ex) {
    // handle exception
    return null;
  }
};

Now that we've wrapped the code into a Func, it can be passed into a generic method that does nothing more than call the func and send the return value back to the caller.
T ListScope<T>(Func<T> ScopedFunction)
{
    T result = ScopedFunction();
    return result;
}

It would seem like this is a pointless process, just to pass a function into another method which calls the passed function. However, since the compiler is able to determine the return type of the inner function, it can also determine the generic type T. Therefore the result can be assigned to an implicitly-typed variable, which no longer has the scoping issue since the entire inner scope was passed in and handled by another method:
var theData = ListScope(() => {
  try {
    return GetData();
  } catch (Exception ex) {
    // handle exception
    return null;
  }
};

Notice that I did not have to define the type anywhere in the above code, and I'm able to pull the value out of the try..catch into variable theData. I've found this method so useful that it is a static utility method in all of my projects. You can also use this for other things besides try..catch, because it's flexible enough to accommodate any scenario:
// if statement
var result =
    LiftScope(() => {
        if (a == b)
            return GetData(true);
        else if (b == c)
            return GetData(false);
        else
            return GetData(true, 2);
    });

// using statement
var result = 
    LiftScope(() => {
        using (var myContext = new MyDataContext())
        {
            return myContext.MyTable.Where(w => w.A == B).ToList();
        }
    });
Happy programming!

No comments:

Post a Comment