bedo
bedo

Reputation: 876

Why is casting a dynamic of type object to object throwing a null reference exception?

I have the following function:

public static T TryGetArrayValue<T>(object[] array_, int index_)
{
    ... //some checking goes up here not relevant to question

    dynamic boxed = array_[index_];
    return (T)boxed;
}

When I call it in the following way,

object a = new object();
object v = TUtils.TryGetArrayValue<object>(new object[] { a }, 0);

(T)boxed throws a null reference exception.

Any other type I put in there other than "object", it works perfectly fine.
Any ideas what this is, and why it's throwing the exception?

Edit: The reason why I use dynamic is to avoid exception when converting types, for example:

double a = 123;
int v = TUtils.TryGetArrayValue<int>(new object[] { a }, 0);

Upvotes: 62

Views: 3425

Answers (4)

Eric Lippert
Eric Lippert

Reputation: 660138

I agree with the other answerers who say that this looks like a bug. Specifically it appears to be a bug in C# runtime binding layer, though I have not investigated it thoroughly.

I apologize for the error. I'll report it to the C# 5 testing team and we'll see if it has already been reported and fixed in C# 5. (It reproduces in the recent beta release, so it is unlikely that it has already been reported and fixed.) If not, a fix is unlikely to make it into the final release. In that case we'll consider it for a possible servicing release.

Thanks for bringing this to our attention. If you feel like entering a Connect issue to track it, feel free to do so and please include a link to this StackOverflow question. If you don't, no problem; the test team will know about it either way.

Upvotes: 45

Elian Ebbing
Elian Ebbing

Reputation: 19037

This is really strange behavior, and it indeed looks like a bug in the implementation of dynamic. I discovered that this variation does not throw an exception and indeed returns the object:

public static T TryGetArrayValue<T>(object[] array, int index) where T : class
{
    dynamic boxed = array[index];
    return boxed as T;
}

Note that I had to add a generic constraint in the method signature because the as operator only works if T is a reference type.

If you are looking for a workaround, you could use this (and I know it's ugly):

public static T TryGetArrayValue<T>(object[] array, int index)
{
    dynamic boxed = array[index];

    if (typeof(T) == typeof(object))
        return (T)(boxed as object);

    return (T)boxed;
}

Upvotes: 6

Reed Copsey
Reed Copsey

Reputation: 564433

This is an issue with how dynamic works - the runtime binder has an issue with conversions from System.Object, but in practice, is really not an issue.

I suspect this is because dynamic, at runtime, is itself always System.Object. The C# Language Spec in 4.7 states: "The type dynamic is indistinguishable from object at run-time." As such, any object used as dynamic is just stored as an object.

When you put an actual instance of System.Object into a dynamic, something is occuring within the runtime binding resolution which causes a null reference exception.

However, any other type that isn't System.Object works - even reference types and the like, without flaws. As such, this should provide you the proper behavior, since there really are no reasons to create an instance of System.Object itself which would be passed around - you'd always want some subclass with other type information.

As soon as you use any "real" type, this works fine. For exmaple, the following works, even though it's passed and treated as Object:

public class Program
{
    public static T TryGetArrayValue<T>(object[] array_, int index_)
    {

        dynamic boxed = array_[index_];
        return (T)boxed;
    }

    private static void Main()
    {
        int p = 3;
        object a = p;
        var objects = new[] { a, 4.5 };

        // This works now, since the object is pointing to a class instance
        object v = TryGetArrayValue<object>(objects, 0);
        Console.WriteLine(v);

        // These both also work fine...
        double d = TryGetArrayValue<double>(objects, 1);
        Console.WriteLine(d);
        // Even the "automatic" int conversion works now
        int i = TryGetArrayValue<int>(objects, 1);
        Console.WriteLine(i);
        Console.ReadKey();
    }
}

Upvotes: 14

Jetti
Jetti

Reputation: 2458

It has something to do with the dynamic keyword. If I change the type to T for boxed it works.

    static void Main(string[] args)
    {
        object a = new object();
        object v = TryGetArrayValue<object>(new object[] { a }, 0);

        Console.ReadLine();
    }

    public static T TryGetArrayValue<T>(object[] array_, int index_)
    {

            T boxed = (T)array_[index_];
            return boxed;

    }

Is there a particular reason that you are using dynamic? You really don't need it in this case as you know what the type is ahead of time. If you look, in your version the type of boxed isn't object but it is dynamic{object} which could be the issue when trying to cast to object. If you look at this version that I posted, you get a type of object and no errors.

Upvotes: 2

Related Questions