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

In C # 7 it is possible to throw an exception inside an expression:

int n = list != null ? list.Count : throw new NullReferenceException("list");

      

At this position, the throw expression can replace any type of expression.

Now I want to define a function that does some action before throwing an exception:

??? DoSomethingAndThrowException(Exception e)
{
    MessageBox.Show("Prepare for an exception!");
    throw e;
}

      

What would the return type of such a function be 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"));

      

It is possible to declare it as a generic method:

T DoSomethingAndThrowException<T>(Exception e) {...}

      

But this seems cumbersome, since the generic type does not appear anywhere in the function body. Is this the only way to do it, or is there some type that I am not aware of that is assigned to any type (so called "anti-object" type)?

+3


source to share


4 answers


The type you are talking about is known as the lower type , a subtype of all types, as opposed to the upper type , a supertype of all types.

As you noted, the top type in C # is called object

*. On the other hand, C # doesn't have a bottom type (although there is a suggestion to add one ).

When in fact there is one type that has the implicit conversion to any other type: dynamic

. This means that if you set the return type DoSomethingAndThrowException

to dynamic

, your code will compile. But I don't think it dynamic

's a good choice here because it's too contagious. For example, if you used var

( var n = list != null ? list.Count : DoSomethingAndThrowException(new NullReferenceException("list"));

) in your statement , then the type n

would be dynamic

, with all the luggage bringing.



I think this means your conclusion is correct: generics is your best bet.

* Technically object

not an upper type because it is object

not a supertype of pointer types. But I don't think this distinction matters much.

+6


source


There is a solution to keep your template. The only change is that you are throwing your method, not throwing it inside the method. Do not overdo it!

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"));

      

+1


source


A ternary operator will not compile unless both expressions return the same type or convert from one type to another. So it doesn't matter what the type of the expression is throw

. If it DoSomethingAndThrowException

does not return, int

or something that can be implicitly converted to int

, it will not compile.

Ternary operator just for convenience, shorthand for operator if

. But in this case it is clearly not convenient, so there is no benefit in using it or writing additional code elsewhere for you to use.

This would be clearer:

int n;
if(list!=null)
    n = list.Count;
else
    DoSomethingAndThrowException(new NullReferenceException("list"));

      

Better,

if(list==null) DoSomethingAndThrowException(new NullReferenceException("list"));
var n = list.Count;

      

It would be even more explicit and clear:

if(list==null)
{
    DoSomething();
    throw new NullReferenceException("list");
}
var n = list.Count();

      

This may seem like splitting hair, but in such cases it is often better to follow typical conventions than do something creative that works the same way. Doing the same thing, but someone reading the code later might spend ten extra seconds trying to figure out what's going on.

0


source


Here is a short specification for the throw-expression function .

Relevant part of your question:

The type rules are as follows:

  • The throw_expression is not type.
  • The throw_expression value is converted to each type using an implicit conversion.
0


source







All Articles