6

I like using implicit typing for almost everything because it's clean and simple. However, when I need to wrap a try...catch block around a single statement, I have to break the implicit typing in order to ensure the variable has a defined value. Here's a contrived hypothetical example:

var s = "abc";

// I want to avoid explicit typing here
IQueryable<ABC> result = null;
try {
    result = GetData();
} catch (Exception ex) { }

if (result != null)
    return result.Single().MyProperty;
else
    return 0;

Is there a way I can call GetData() with exception handling, but without having to explicitly define the type of the result variable? Something like GetData().NullOnException()?

mellamokb
  • 56,094
  • 12
  • 110
  • 136
  • You mean like adding extension method? – tia Dec 28 '12 at 17:18
  • @tia: I'm just thinking that might be one way of implementing it. Ideally I'd like to be able to say `var result = GetData()` while having exception handling. – mellamokb Dec 28 '12 at 17:19
  • A "silly" solution is to declare `result` in the same place, before the `try`, like so: `var result = (IQueryable)null;` But that's _more_ typing than what you have. It is only when the local variable is not assigned to in the same line where it is declared, that it's abslutely impossible to use `var`. – Jeppe Stig Nielsen Jan 15 '13 at 21:40

3 Answers3

9

This is a common problem. I recommend that you just stick with your existing solution.

If you really want an alternative, here it is:

static T NullOnException<T>(Func<T> producer) where T : class {
  try { return producer(); } catch { return null; } //please modify the catch!
}

//now call it
var result = NullOnException(() => GetData());

Please modify this to log the exception or restrict the catch to a concrete type. I do not endorse swallowing all exceptions.

As this answer is being read a lot I want to point out that this implementation is just of demo-quality. In production code you probably should incorporate the suggestions given in the comments. Write yourself a robust, well-designed helper function that will serve you well for years.

usr
  • 168,620
  • 35
  • 240
  • 369
  • +1 This is kind of what I was thinking, but wasn't quite sure how to do it. – mellamokb Dec 28 '12 at 17:39
  • 2
    Perhaps add a second `Action` for handling the exception. I assumed the catch block was only empty here because it didn't affect the question, not because there really is nothing there. – Servy Dec 28 '12 at 17:39
  • @Servy I fully agree. I didn't take the time to flesh out this sample code. In my production code I usually have a result type of `Tuple`. – usr Dec 28 '12 at 17:41
  • @usr Yeah, if you had an `Action` you could always put it into a closed over local if you needed to bring it out into a higher scope as well. It's mostly personal preference I guess. – Servy Dec 28 '12 at 17:42
  • 1
    Oh, and you could return `default(T)` (or have an optional parameter for the default) instead of `null` so that you don't need the restriction of `class`. – Servy Dec 28 '12 at 17:43
  • @usr: Here's another similar scenario I run into often. I want to connect to a database and get some data, so I wrap a couple lines of code in a `using` block. Once again, in order to carry the result into the outer scope, I have to explicitly pre-define the variable with a `null` value. Do you have a nice way to handle this? I'm thinking something like a custom `WrapUsing()` extension method, or a general-purpose `LiftScope()` helper method. – mellamokb Dec 29 '12 at 03:23
2

Just put your code inside the try:

var s = "abc";

// I want to avoid explicit typing here
try {
    var result = GetData();
    if (result != null)
        return result.Single().MyProperty;
    else
        return 0;
} catch (Exception ex) { }
Michael Bray
  • 14,998
  • 7
  • 42
  • 68
  • 1
    My contrived example does make it a little too easy :) Suppose there was another 30 lines of code yet, and I didn't really want to wrap it all in the try...catch. – mellamokb Dec 28 '12 at 17:20
  • @mellamokb Then you're outside the scope and can't use `var`? Maybe you need to come up with a better example or something. – Pete Dec 28 '12 at 17:21
  • You could extract the 'extra' code into a separate function, but that never feels right to me, if it logically belongs in the function. – Michael Bray Dec 28 '12 at 17:21
  • BTW, I personally think that using implicit typing is ok to quickly write code that you can't remember what the type is, or you are unsure, etc... But I always convert back to explicit type. (I use Ctrl-` but I can't remember if this is built-in or if it's an add-on.) – Michael Bray Dec 28 '12 at 17:24
  • @MichaelBray: Is that something from ReSharper (which I don't have)? I don't see such a shortcut available for Visual Studio: http://stackoverflow.com/questions/9101647/convert-var-to-explicit-type-in-visual-studio – mellamokb Dec 28 '12 at 17:33
  • 1
    @mellamokb: Hmmmm I think maybe CodeRush Xpress version or something... When I use that command, it gives me a big red semi-transparent arrow pointed at the new explicit type name and says "Make Explicit" so it definitely isn't built in. – Michael Bray Dec 28 '12 at 17:44
0

I came to a similar solution as @usr, but with slightly different semantics:

T LiftScope<T>(Func<T> ScopedFunction)
{
    T result = ScopedFunction();
    return result;
}

The purpose of LiftScope is to carry an internal variable out to the caller without compromising implicit typing. This could be used to solve the original problem, except that the try...catch would actually be embedded in the call.

try...catch

var result = 
    LiftScope(() => {
        try { return producer(); } catch { return null; }
    });

Now the caller is able to be responsible for exception handling. Furthermore, this can be used generically in a handful of similar use-cases where you have very short-lived scopes.

if

var result =
    LiftScope(() => {
        if (a == b)
            return GetData(true);
        else if (b == c)
            return GetData(false);
        else
            return GetData(true, 2);
    });

This could also be solved with a ternary-style if statement.

using

var result = 
    LiftScope(() => {
        using (var myContext = new MyDataContext())
        {
            return myContext.MyTable.Where(w => w.A == B).ToList();
        }
    });
mellamokb
  • 56,094
  • 12
  • 110
  • 136