62

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);
bedo
  • 866
  • 8
  • 15

4 Answers4

45

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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    Can I get an honorable mention for discovering the bug? :D – bedo Mar 29 '12 at 19:54
  • I just marked another question as a duplicate of this one. Was a bug reported to Connect? If so is there a link where the status can be tracked? – D Stanley May 24 '16 at 19:04
  • @DStanley: I mentioned it to the test team back in 2012 but I have no memory whatsoever of how the issue was triaged, sorry. – Eric Lippert May 24 '16 at 20:08
  • No worries; just seemed an odd bug and was curious if it was fixed. Finding reported bugs on Connect is a skill I have yet to master. – D Stanley May 24 '16 at 20:11
  • Still exists in .NET Core as well. – Sean Oct 28 '16 at 16:09
14

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();
    }
}
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
6

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;
}
Elian Ebbing
  • 18,779
  • 5
  • 48
  • 56
  • Unfortunately I need it to work for primitives as well. Maybe I can just handle object as a special case, and not cast when I return. – bedo Mar 29 '12 at 19:18
  • This doesn't even compile if you are trying to use `TryGetArrayValue(...) – Jetti Mar 29 '12 at 19:18
  • @bedo - I extended my answer to show how you can work around this problem by using the `as` keyword. – Elian Ebbing Mar 29 '12 at 19:28
  • Yes, that works. I figued handling object as a special case was the way to go. I was just very curious why it threw the exception. Given that it will be very unlikely a simple object will be passed in (I don't intend on passing in any objects used for synchronization around), I can just live with it throwing the null ref exception and noting it with a comment. – bedo Mar 29 '12 at 19:41
2

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.

Jetti
  • 2,418
  • 1
  • 17
  • 25
  • Yes, I have a use case for using dynamic, because I want to do something like this: double a = 10923049; int v = TUtils.TryGetArrayValue(new object[] { a }, 0); using dynamic helps me perform the cast from a different compatible type. Otherwise it throws an exception. – bedo Mar 29 '12 at 19:10
  • @bedo with a method called TryGetArrayValue, I would expect that it should be able to fail. Seeing your intention is just making me cringe! Although I'm a big fan of static typing and compile type type checks. – Jetti Mar 29 '12 at 19:16