Helmut D
Helmut D

Reputation: 680

What is the compile-time type of the "throw" expression in C# 7?

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

Answers (4)

Julien Couvreur
Julien Couvreur

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

Mafii
Mafii

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

Scott Hannen
Scott Hannen

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

svick
svick

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

Related Questions