Reputation: 680
In C# 7 it is possible to raise an exception inside an expression:
int n = list != null ? list.Count : throw new NullReferenceException("list");
In this position, the throw expression can substitute an expression of any type.
Now I want to define a function that executes some action before raising an exception:
??? DoSomethingAndThrowException(Exception e)
{
MessageBox.Show("Prepare for an exception!");
throw e;
}
What would be the return type of such function, so that it can be used in the same places as the original throw expression:
int n = list != null ? list.Count : DoSomethingAndThrowException(new NullReferenceException("list"));
There's the option to declare it as a generic method:
T DoSomethingAndThrowException<T>(Exception e) {...}
But this seems cumbersome, as the generic type doesn't occur anywhere in the function body. Is this the only way to do it, or is there some type I don't know of, and which is assignable to any type (an "anti-object" type, so to speak)?
Upvotes: 3
Views: 329
Reputation: 4983
Here is the short specification for the throw-expression feature.
The relevant portion to your question:
The type rules are as follows:
- A throw_expression has no type.
- A throw_expression is convertible to every type by an implicit conversion.
Upvotes: 0
Reputation: 7455
There is a solution that allows you to keep your pattern. The only change is that you throw your method, instead of throwing inside of the method. Don't overthink things!
private static Exception DoSomethingAndReturnException(Exception exception)
{
// Do something
// in the end, return the exception
return exception;
}
You can use it like this:
int n = (list != null) ? list.Count : throw DoSomethingAndReturnException(new NullReferenceException("list"));
Upvotes: 1
Reputation: 29262
The ternary operator won't compile unless both expressions return the same type or there's a conversion from one type to the other. So it doesn't really matter what the type of the throw
expression is. Unless DoSomethingAndThrowException
returns int
or something which can be implicitly converted to int
it won't compile.
The ternary operator is just for convenience, shorthand for an if
statement. But in this case it's evidently not convenient, so there's no benefit to using it or writing extra code in other places so that you can use it.
This would be clearer:
int n;
if(list!=null)
n = list.Count;
else
DoSomethingAndThrowException(new NullReferenceException("list"));
Better yet,
if(list==null) DoSomethingAndThrowException(new NullReferenceException("list"));
var n = list.Count;
This would be even more explicit and clear:
if(list==null)
{
DoSomething();
throw new NullReferenceException("list");
}
var n = list.Count();
It may seem like splitting hairs, but cases like these it's often better to follow the typical conventions than to do something creative that works the same way. The execution is the same, but someone reading the code later could spend ten extra seconds trying to figure out what's going on.
Upvotes: 0
Reputation: 244988
The type you're talking about is known as the bottom type, the subtype of all types, as opposed to the top type, the supertype of all types.
As you noted, the top type in C# is called object
*. On the other hand, C# does not have the bottom type (though there is a proposal to add it).
Though there actually is one type that has implicit conversions to any other type: dynamic
. This means that if you set the return type of DoSomethingAndThrowException
to dynamic
, your code would compile. But I don't think dynamic
is a good choice here, because it's too infectious. For example, if you used var
in your statement (var n = list != null ? list.Count : DoSomethingAndThrowException(new NullReferenceException("list"));
), then the type of n
would be dynamic
, with all the baggage that brings.
I think this means that your conclusion is correct: generics are your best option.
* Technically, object
is not the top type, because object
is not the supertype of pointer types. But I think that's a distinction that doesn't matter much.
Upvotes: 6