14

I wonder if someone could explain why in this code

public class SomeClass
{
    public T GenericMethod<T>(dynamic value)
    {
        return (T)value;
    }
}

the 'return value;' statement throws an null reference exception when called with:

new SomeClass().GenericMethod<object>(new object()); // throws System.NullReferenceException

It works as expected when called with:

new SomeClass().GenericMethod<string>("SomeString"); // returns SomeString
new SomeClass().GenericMethod<object>("SomeString"); // returns SomeString

Note: The following compiles and runs just fine

public class SomeOtherClass
{
    public T GenericMethod<T>(object value)
    {
        return (T)value;
    }
}

The stacktrace:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.CSharp.RuntimeBinder.ExpressionTreeCallRewriter.GenerateLambda(EXPRCALL pExpr)
   at Microsoft.CSharp.RuntimeBinder.Semantics.ExprVisitorBase.Visit(EXPR pExpr)
   at Microsoft.CSharp.RuntimeBinder.ExpressionTreeCallRewriter.Rewrite(TypeManager typeManager, EXPR pExpr, IEnumerable`1 listOfParameters)
   at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.BindCore(DynamicMetaObjectBinder payload, IEnumerable`1 parameters, DynamicMetaObject[] args, DynamicMetaObject& deferredBinding)
   at Microsoft.CSharp.RuntimeBinder.RuntimeBinder.Bind(DynamicMetaObjectBinder payload, IEnumerable`1 parameters, DynamicMetaObject[] args, DynamicMetaObject& deferredBinding)
   at Microsoft.CSharp.RuntimeBinder.BinderHelper.Bind(DynamicMetaObjectBinder action, RuntimeBinder binder, IEnumerable`1 args, IEnumerable`1 arginfos, DynamicMetaObject onBindingError)
   at Microsoft.CSharp.RuntimeBinder.CSharpConvertBinder.FallbackConvert(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
   at System.Dynamic.DynamicMetaObject.BindConvert(ConvertBinder binder)
   at System.Dynamic.DynamicMetaObjectBinder.Bind(Object[] args, ReadOnlyCollection`1 parameters, LabelTarget returnLabel)
   at System.Runtime.CompilerServices.CallSiteBinder.BindCore[T](CallSite`1 site, Object[] args)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
pvill
  • 185
  • 11
  • Nice find! Hopefully some compiler expert has an explanation. – René Vogt May 23 '16 at 13:46
  • 1
    I suspect it has something to do with the dynamic cast to `T` (even though the actual type is the same). If you do `return value as T` it works as well. – D Stanley May 23 '16 at 13:58
  • The dynamic keyword tells the compiler not to enforce additional rules upon your code, which is why I think this is allowed to compile. As for the actual return type, it looks like the value input argument holds a value, and it appears as though you can cast the value (`value as T` works, for example) – J Robson May 23 '16 at 13:58
  • `value as T` gives a compiler error `The 'as' operator cannot be used with a non-reference type parameter 'T'` – pvill May 23 '16 at 14:05
  • Also changing the statement to `return (T)value;` has the same behaviour – pvill May 23 '16 at 14:11
  • @pvill Ah, true - I had added a `where T:class` constraint as an experiment and had not removed it. A reference cast (`return (T)(object)value`) works without that constraint, though. – D Stanley May 23 '16 at 14:13
  • Interestingly enough, calling `AMethod(new object())` results in an `InvalidCastException`, not a `NullReferenceException`. I wonder if you found a bug in the .NET Framework runtime? – xofz May 23 '16 at 21:46
  • @Sam Pearson That is something I would expect. When the argument is an Object and you want to cast it to a string, an invalidcastexception is the most logical result. The dynamic keyword forces the compiler not to complain about it, but it is still thrown runtime. – pvill May 24 '16 at 07:02
  • 1
    It appears this bug was found in 2012, it appears it's still not fixed http://stackoverflow.com/questions/9931624/why-is-casting-a-dynamic-of-type-object-to-object-throwing-a-null-reference-exce – mark_h May 24 '16 at 18:51

2 Answers2

3

It appears it's a bug first reported in 2012 but still apparently not fixed (as of May 24th 2016)

Community
  • 1
  • 1
mark_h
  • 5,233
  • 4
  • 36
  • 52
1

Update

As the marked duplicate indicates this is a known bug in the .NET runtime.


The problem is likely a poor indication of an invalid run-time cast. If you take out the dynamic binding and make the parameter type object you get the compiler error

Cannot implicitly convert type 'object' to 'T'. An explicit conversion exists (are you missing a cast?)

Since by adding dynamic this casting is done at run-time, this implicit conversion error manifests itself differently at run-time with a vague NullReferenceException in the run-time bindings.

I'm not an expert in the inner workings of the DLR, but I suspect that an object passed as a dynamic value is not really a pure object at run-time. I suspect that it's some sort of wrapper around object, and thus can't be implicitly cast to object at run-time.

An explicit reference cast

return (T)(object)value;

will not generate that error.

Community
  • 1
  • 1
D Stanley
  • 149,601
  • 11
  • 178
  • 240
  • That is correct `return (T)(object)value;` removes the error but what is the reason `return (T)value` doesn't. Why is the extra cast to (object) needed – pvill May 23 '16 at 14:14
  • @pvill because the same cast operator is also used to do conversions in some cases (e.g. `int i = 1; long l = (long)l;`). It's covered in section 6.2.7 of the C# specification. Adding the cast to object specifies that the operation is a true reference cast and not an implicit conversion. – D Stanley May 23 '16 at 14:18
  • That kind of conversion specified in that particular section should not be necessary here. `new SomeClass().GenericMethod(10);` works just fine without the modification. And `value` is of type object and `T` is also Object. There should be no conversion ... – pvill May 23 '16 at 14:31