2

I encountered a bizarre bug (?) while refactoring some code.

For some unknown reason, I get a compiler error when passing a dynamic variable to a static method in a throw statement.

dynamic result = comObj.getfoo(bar);

// ....

throw ApiException.FromResult(result);

Error CS0155
The type caught or thrown must be derived from System.Exception

This makes no sense as the returned value is derived from System.Exception.

Furthermore, what makes me lean toward this being a bug, is that error can be circumvented by either:

  1. changing dynamic to var

  var result = 0;
  1. by casting the variable to object.

  throw ApiException.FromResult((object)result);

Any ideas?


Live on dotnetfiddle

/* PS: code omitted for brevity */

using System;

public class Program
{

    public class ApiException : Exception
    {

        public static ApiException FromResult(object result)
        {
            return new ApiException();
        }

    }

    [STAThread]
    public static void Main(string[] args)
    {       
        dynamic result = 0;
        throw ApiException.FromResult(result);  
    }

}
Bjørn-Roger Kringsjå
  • 9,849
  • 6
  • 36
  • 64

2 Answers2

4

This is "expected" behavior as any statement containing dynamic is treated as if its result type is dynamic, which can't be cast to Exception at compile time.

dynamic x = new Exception();
throw x; // fails to compile as x's type 
         // is not known to derive from `Exception` at compile time

throw (Exception)x; // works

Your workarounds change compile-time result from dynamic to proper type and hence compile correctly.

dynamic result =....
throw 
      // compile type here is "dynamic" as one parameter is `dynamic`
      ApiException.FromResult(result);

This works because var is not dynamic (What's the difference between dynamic(C# 4) and var?)

var result = ... // any non dynamic type - staticly know at compile time
throw 
      // compile type here is ApiException because type of parameter is known
      // at compile time 
      ApiException.FromResult(result);

Similar problem happen when you try to invoke extensions on dynamic result - Extension method and dynamic object


To address concern why "throw" is different from function call (that happily handle resolution at run-time): specification for throw statement requires type of expression to be Exception unlike for expressions including method invocation where there are rules for dynamic binding (7.2. Static and Dynamic Binding).

Interesting note that other statement like while are happy with dynamic value at compile time and I don't see reason why they behave differently.

Excerts from C# 5 spec

7.2 Static and Dynamic Binding
...
When an operation is dynamically bound, little or no checking is performed by the compiler. Instead if the run-time binding fails, errors are reported as exceptions at run-time.
The following operations in C# are subject to binding:
• Method invocation: e.M(e1,…,en)
...
8.9.5 The throw statement:
...
The expression must denote a value of the class type System.Exception, of a class type that derives from System.Exception or of a type parameter type that has System.Exception (or a subclass thereof) as its effective base class.

Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • That's it. So, should I blame the IDE then? When I hover `FromResult` it still reads `ApiException`. – Bjørn-Roger Kringsjå Oct 13 '16 at 17:20
  • @Bjørn-RogerKringsjå sure... Also at that point IDE has hard choice of trying to be useful in general and showing type for method or being 100% compatible with compiler which probably require more info than intellisense has at that point. I believe intellisense for `dynamic` is best effort as everything can change at run time so I would not expect much from it. – Alexei Levenkov Oct 13 '16 at 17:45
  • "[an object of type dynamic bypasses static type checking](https://msdn.microsoft.com/en-ca/library/dd264736.aspx)"; does this mean that dynamic bypasses compile-time static type checking *except* for `Exception` types? Or are these statements different than "static type checking": "// fails to compile as x's type // is not known to derive from `Exception` at compile time"? – Quantic Oct 13 '16 at 18:05
  • @Quantic I added my understanding of the reason to the post - `throw` is statement with particular requirement which don't have special case for `dynamic`. – Alexei Levenkov Oct 13 '16 at 19:00
2

Most of the operations involving dynamic result in dynamic as result type, hence you see the error. (and as explained in the other answer).

But it is not that all operations involving dynamic results in dynamic type, for example with constructor. If you have a constructor in your class:

public ApiException(object result)
{

}

and you try to invoke it like:

dynamic result = 0;
throw new ApiException(result);

There will be no error.

See: Using Type dynamic (C# Programming Guide)

The result of most dynamic operations is itself dynamic.

and also:

Operations in which the result is not dynamic include conversions from dynamic to another type, and constructor calls that include arguments of type dynamic.

The reason it works with var is because it is a strongly typed variable at the time of compilation.

Community
  • 1
  • 1
Habib
  • 219,104
  • 29
  • 407
  • 436