为什么将对象类型的动态转换为对象会引发空引用异常?

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

提问人:bedo 提问时间:3/30/2012 最后编辑:bedo 更新时间:3/30/2012 访问量:3207

问:

我有以下功能:

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;
}

当我按以下方式调用它时,

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

(T)boxed引发 null 引用异常。

除了“对象”之外,我放在那里的任何其他类型,它都工作得很好。
知道这是什么,为什么它会抛出异常吗?

编辑: 我之所以使用dynamic,是为了避免在转换类型时出现异常,例如:

double a = 123;
int v = TUtils.TryGetArrayValue<int>(new object[] { a }, 0);
C# .NET 动态 NullReferenceException

评论

0赞 John Saunders 3/30/2012
在强制转换之前检查是否为 null。boxed
5赞 dlev 3/30/2012
这可能是实现方式中的错误。现在检查这是否在 4.5 中重现。dynamic
1赞 BrokenGlass 3/30/2012
重现 - 它看起来确实像是如何处理的错误 - 这个问题是有效的dynamic
1赞 ahawker 3/30/2012
+1 - 似乎仅在转换为泛型类型时重现。

答:

2赞 Jetti 3/30/2012 #1

它与动态关键字有关。如果我将盒装的类型更改为 T,它就可以工作。

    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;

    }

您使用动态有什么特殊原因吗?在这种情况下,您真的不需要它,因为您提前知道类型是什么。如果你看一下,在你的版本中,盒装的类型不是对象,而是动态的{object},这可能是尝试强制转换为对象时的问题。如果你看一下我发布的这个版本,你会得到一种类型的对象,没有错误。

评论

0赞 bedo 3/30/2012
是的,我有一个使用动态的用例,因为我想做这样的事情:double a = 10923049;int v = TUtils.TryGetArrayValue<int>(new object[] { a }, 0);使用动态可以帮助我从不同的兼容类型执行强制转换。否则,它将引发异常。
0赞 Jetti 3/30/2012
@bedo使用名为 TryGetArrayValue 的方法,我希望它应该能够失败。看到你的意图只会让我畏缩!虽然我是静态类型和编译类型类型检查的忠实粉丝。
6赞 Elian Ebbing 3/30/2012 #2

这确实是奇怪的行为,而且看起来确实像是实现中的错误。我发现这种变体不会抛出异常,并且确实返回了对象:dynamic

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

请注意,我必须在方法签名中添加泛型约束,因为运算符仅在引用类型时才有效。asT

如果你正在寻找一个解决方法,你可以使用它(我知道它很丑陋):

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;
}

评论

0赞 bedo 3/30/2012
不幸的是,我也需要它来为原语工作。也许我可以把对象作为特例处理,而不是在我回来时投射。
0赞 Jetti 3/30/2012
如果您尝试使用“TryGetArrayValue<int>(...),这甚至不会编译
0赞 Elian Ebbing 3/30/2012
@bedo - 我扩展了我的答案,以展示如何使用关键字来解决此问题。as
0赞 bedo 3/30/2012
是的,这行得通。我认为将对象作为特殊情况处理是要走的路。我只是很好奇为什么它会抛出异常。鉴于不太可能传入一个简单的对象(我不打算传入任何用于同步的对象),我可以忍受它抛出 null ref 异常并用注释记录它。
14赞 Reed Copsey 3/30/2012 #3

这是动态工作方式的问题 - 运行时活页夹在转换 时存在问题,但实际上,这确实不是问题。System.Object

我怀疑这是因为,在运行时,它本身总是.4.7 中的 C# 语言规范指出:“类型动态在运行时与对象无法区分。因此,任何用作动态的对象都只是存储为对象。dynamicSystem.Object

将 的实际实例放入动态实例时,运行时绑定解析中会发生某些情况,从而导致 null 引用异常。System.Object

但是,任何其他不起作用的类型 - 甚至是引用类型等,都没有缺陷。因此,这应该为您提供正确的行为,因为实际上没有理由创建会传递的自身实例 - 您总是需要一些具有其他类型信息的子类。System.ObjectSystem.Object

只要您使用任何“真实”类型,就可以正常工作。例如,以下工作有效,即使它被传递并被视为: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();
    }
}

评论

0赞 Reed Copsey 3/30/2012
@bedo我不太明白为什么它在对象上失败,但正如我所说,在实践中,至少;)
45赞 Eric Lippert 3/30/2012 #4

我同意其他回答者的说法,他们说这看起来像一个错误。具体来说,它似乎是 C# 运行时绑定层中的一个错误,尽管我还没有彻底调查它。

对于这个错误,我深表歉意。我会将其报告给 C# 5 测试团队,我们将查看它是否已在 C# 5 中报告和修复。(它在最近的测试版中重现,因此不太可能已经报告和修复。否则,修复程序不太可能进入最终版本。在这种情况下,我们会将其视为可能的服务版本。

感谢您提请我们注意这一点。如果您想输入 Connect 问题来跟踪它,请随时这样做,并请附上指向此 StackOverflow 问题的链接。如果你不这样做,没问题;无论哪种方式,测试团队都会知道它。

评论

1赞 bedo 3/30/2012
我能因为发现这个错误而获得荣誉奖吗?:D
0赞 D Stanley 5/25/2016
我只是将另一个问题标记为这个问题的重复项。是否向 Connect 报告了错误?如果是这样,是否有可以跟踪状态的链接?
0赞 Eric Lippert 5/25/2016
@DStanley:我在 2012 年向测试团队提到过这个问题,但我不记得这个问题是如何分类的,对不起。
0赞 D Stanley 5/25/2016
不用担心;只是看起来是一个奇怪的错误,很好奇它是否被修复。在 Connect 上查找报告的错误是我尚未掌握的一项技能。
0赞 Sean 10/29/2016
仍存在于 .NET Core 中。