34

I have a number of methods doing next:

var result = command.ExecuteScalar() as Int32?;
if(result.HasValue)
{
   return result.Value;
}
else
{
   throw new Exception(); // just an example, in my code I throw my own exception
}

I wish I could use operator ?? like this:

return command.ExecuteScalar() as Int32? ?? throw new Exception();

but it generates a compilation error.

Is it possible to rewrite my code or there is only one way to do that?

user1069816
  • 2,763
  • 2
  • 26
  • 43
abatishchev
  • 98,240
  • 88
  • 296
  • 433
  • I'd like to be able to `return this as T ?? that as T ?? other as T ?? throw new NotSupportedException();` Instead I have to use a temporary variable, test for null, and return the temporary. It's just a bit uglier. – Tergiver Jul 04 '11 at 18:36
  • On Connect() 2016 there is a presentation showing this feature for the upcoming C# 7. – Thomas Nov 25 '16 at 10:13
  • 2
    It seems like you got what you wanted in C#7 http://structuredsight.com/2016/09/01/c-7-additions-throw-expressions/ – Svek Feb 16 '17 at 03:57

5 Answers5

65

For C# 7

In C# 7, throw becomes an expression, so it's fine to use exactly the code described in the question.

For C# 6 and earlier

You can't do that directly in C# 6 and earlier - the second operand of ?? needs to be an expression, not a throw statement.

There are a few alternatives if you're really just trying to find an option which is concise:

You could write:

public static T ThrowException<T>()
{
    throw new Exception(); // Could pass this in
}

And then:

return command.ExecuteScalar() as int? ?? ThrowException<int?>();

I really don't recommend that you do that though... it's pretty horrible and unidiomatic.

How about an extension method:

public static T ThrowIfNull(this T value)
{
    if (value == null)
    {
        throw new Exception(); // Use a better exception of course
    }
    return value;
}

Then:

return (command.ExecuteScalar() as int?).ThrowIfNull();

Yet another alternative (again an extension method):

public static T? CastOrThrow<T>(this object x) 
    where T : struct
{
    T? ret = x as T?;
    if (ret == null)
    {
        throw new Exception(); // Again, get a better exception
    }
    return ret;
}

Call with:

return command.ExecuteScalar().CastOrThrow<int>();

It's somewhat ugly because you can't specify int? as the type argument...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I think it was because you didn't answer as Tony. Anyway, countered it for you. You're on the right track here, but Think there is a nicer, generalised, technique that I'll add as my own response(at the risk of a downvote) – philsquared Nov 19 '09 at 12:18
  • Jon, could you use generic parameter constraints to create two `CastOrThrow` methods, one for value types/structs and one for reference types? The former would use `T?` whereas the latter would use `T`. – Adam Maras Nov 19 '09 at 12:24
  • @Adam: Unfortunately, you can't have two methods where the only difference to their signature is the output type and/or generic constraints. – LukeH Nov 19 '09 at 12:54
  • 1
    Extension method! Simply brilliant `ThrowIfNull` +1 – Alex Bagnolini Nov 19 '09 at 18:34
  • Hi @JonSkeet you may want to update this answer with a hint to the (in C#7) added feature of doing what OP wants: http://structuredsight.com/2016/09/01/c-7-additions-throw-expressions/ – Mafii Feb 24 '17 at 16:03
  • @Mafii: Will do in this case, although I don't intend to go through all my 7-year-old answers to check them... – Jon Skeet Feb 24 '17 at 17:10
9

As has been said, you can't do this with the ?? operator (well, not without some contortions that don't seem to fit with your aim of making this cleaner).

When I see this pattern emerging I immediately think of Enforcements. Originally from the C++ world they transfer to C# pretty well, although are arguably less important most of the time.

The idea is that you take something of the form:

if( condition )
{
  throw Exception;
}

and converts it to:

Enforce<Exception>( condition );

(you can further simplify by defaulting the exception type).

Taking it further you can write a set of Nunit-style methods for different condition checks, e.g.;

Enforce<Exception>.NotNull( obj );
Enforce<Exception>.Equal( actual, expected );
Enforce<Exception>.NotEqual( actual, expected );

etc.

Or, better still by providing an expectation lamba:

Enforce<Exception>( actual, expectation );

What's really neat is that, once you've done that, you can return the the actual param and enforce inline:

return Enforce( command.ExecuteScalar() as Int32?, (o) => o.HasValue ).Value;

... and this seems to be the closest to what you're after.

I've knocked up an implementation of this before. There's a couple of little niggles, like how you generically create an exception object that takes arguments - some choices there (I chose reflection at the time, but passing a factory in as an extra parameter may be even better). But in general it's all pretty straightforward and can really clean up a lot of code.

It's on my list of things to do to knock up an open source implementation.

philsquared
  • 22,403
  • 12
  • 69
  • 98
  • 1
    Any reason not to use an extension method here? "return (command.ExecuteScalar() as int?).Enforce(x => x.HasValue);" reads slightly better to me... although it may be worth changing the name at that point though. I do like the idea of using a predicate. – Jon Skeet Nov 19 '09 at 13:46
  • Mostly because when I first did this in C# I was using C#2 ;-) I didn't use the lambda for the predicate originally for the same reason, but that was a no brainer to move to. I think the extension method can be made to work nicely, but will have to play with that a bit. – philsquared Nov 19 '09 at 14:23
4

If you just want an exception when the returned value isn't an Int32 then do this:

return (int)command.ExecuteScalar();

If you want to throw your own custom exception then I'd probably do something like this instead:

int? result = command.ExecuteScalar() as int?;
if (result == null) throw new YourCustomException();
return result.Value;
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • Yes, an exception will be thrown. But perhaps an invalid cast exception isn't the appropriate exception to be thrown; it's possible there needs to be an application-specific exception in the event that that command doesn't return a value. – Adam Maras Nov 19 '09 at 11:55
  • @Adam: I really don't think that merits a downvote! The example code in the question throws a plain `Exception`. An `InvalidCastException` or `NullReferenceException` is *more* appropriate and informative than a plain `Exception` with no extra details. – LukeH Nov 19 '09 at 12:02
  • Let me rephrase; it's possible that in the event that the command doesn't return a value, an application-specific exception (for example, a `NoRecordsExistException`) would need to be thrown (yes, I'm aware that the poster didn't mention such a thing, but some people do remove such specificities from their questions.) To do so, one would have to wrap the statement you posted in a `try`/`catch` block, which would defeat the purpose of condensing the code. – Adam Maras Nov 19 '09 at 12:03
  • @Adam: That's true, but without further information from the OP I would reiterate that my code is *simpler* than the code in the question and throws exceptions that are *more* informative/appropriate. If that's not what the OP requires then they should clarify in the question. – LukeH Nov 19 '09 at 12:08
  • I likes this. How about: try { return (int)command.ExecuteScalar(); } catch { throw new NotFoundException(); } – Sedat Kapanoglu Nov 19 '09 at 12:08
  • @Luke: true. I'm a patterns/practices geek, so I'm always looking for the "ideal solutions." I can't change my vote anymore... but you do provide a compelling argument. – Adam Maras Nov 19 '09 at 12:10
  • @ssg: I'm not overly keen on using `try...catch...throw` like that unless absolutely necessary. I've updated the question to show what I probably would do if a custom exception is needed. – LukeH Nov 19 '09 at 12:25
  • @Phil: odd. It let me now, it didn't earlier. I got an error about the vote being too old, and that I couldn't change it unless the answer was modified. Either way, I've changed it now. – Adam Maras Nov 19 '09 at 12:26
  • You could use Execute-Around to remap the exception (that would involve having another method who's purpose it was to call you back, wrapped in a try-catch block, throwing an exception of your choice. But if you're doing that you may as well use my Enforcements proposal anyway :-) – philsquared Nov 19 '09 at 13:31
  • Hello everybody. I edited my post. As Adam mentioned, I wrote Exception just as example. My original code throws my own FinanceResultNotFoundException :) – abatishchev Nov 19 '09 at 18:36
2

You're not going to be able to throw an exception on the right side of the null coalescing operator. The reason behind this is that that the right side of the operator needs to be an expression, not a statement.

The null coalescing operator works like so: if the left value of the operator is null, return it; otherwise, return what's on the right of the operator. The throw keyword doesn't return a value; hence, it can't be used on the right side of the operator.

Adam Maras
  • 26,269
  • 6
  • 65
  • 91
1

The reason you can't do:

return command.ExecuteScalar() as Int32? ?? throw new Exception();

Is because throwing an exception is a statement, not an expression.

If you're just looking to shorten the code a little bit, perhaps this:

var result = command.ExecuteScalar() as Int32?;
if(result.HasValue) return result;
throw new Exception();

No need for the else.

Josh Smeaton
  • 47,939
  • 24
  • 129
  • 164
  • This would only work if the function this return statement is in returns an Object. Any other return type would result in a compiler error, as the left and right expression types of the null coalescing operator are different types. – Adam Maras Nov 19 '09 at 11:58
  • I thought that, hence the 'possibly'. I removed that part of my answer. – Josh Smeaton Nov 19 '09 at 12:00
  • 1
    I prefer inverting the check for value, and throwing if it doesn't have a value. Sounds more logical. – Dykam Nov 19 '09 at 12:12