问题
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?
回答1:
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...
回答2:
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.
回答3:
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;
回答4:
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.
回答5:
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.
来源:https://stackoverflow.com/questions/1762772/is-it-possible-to-use-operator-and-throw-new-exception