English 中文(简体)
Is it possible to use operator ?? and throw new Exception()?
原标题:

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?

最佳回答

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...

问题回答

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.

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;

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.

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.





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签