【问题标题】:Why is casting a dynamic of type object to object throwing a null reference exception?为什么将动态类型的对象转换为抛出空引用异常的对象?
【发布时间】:2012-04-13 11:14:50
【问题描述】:

我有以下功能:

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 抛出空引用异常。

除了“对象”之外,我放入的任何其他类型都可以正常工作。
知道这是什么,为什么会抛出异常?

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

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

【问题讨论】:

  • 在转换之前检查boxed是否为空。
  • 这可能是dynamic 实现方式中的一个错误。现在在 4.5 中检查此重现。
  • Repro - 它看起来确实像处理 dynamic 的错误 - 问题是有效的
  • +1 - 似乎只有在转换为泛型类型时才会重现。

标签: c# .net dynamic nullreferenceexception


【解决方案1】:

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

我为这个错误道歉。我会将它报告给 C# 5 测试团队,我们会看看它是否已经在 C# 5 中报告和修复。(它在最近的 beta 版本中重现,所以它不太可能已经被报告和修复。 ) 如果没有,修复不太可能进入最终版本。在这种情况下,我们会考虑将其作为可能的服务版本。

感谢您提请我们注意。如果您喜欢entering a Connect issue 来跟踪它,请随时这样做,并请提供指向此 StackOverflow 问题的链接。如果你不这样做,没问题;无论哪种方式,测试团队都会知道。

【讨论】:

  • 我能因为发现错误而获得荣誉奖吗? :D
  • 我刚刚将另一个问题标记为与该问题重复。是否向 Connect 报告了错误?如果有,是否有可以跟踪状态的链接?
  • @DStanley:我早在 2012 年就向测试团队提到过它,但我不记得问题是如何分类的,抱歉。
  • 不用担心;只是似乎是一个奇怪的错误,并且很好奇它是否已修复。在 Connect 上查找报告的错误是我尚未掌握的技能。
  • 仍然存在于 .NET Core 中。
【解决方案2】:

这是动态工作方式的问题 - 运行时绑定器在来自 System.Object 的转换方面存在问题,但在实践中,这真的不是问题。

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

当您将System.Object 的实际实例放入动态中时,运行时绑定解析中会发生一些事情,这会导致空引用异常。

但是,任何其他类型 不是System.Object 的作品 - 即使是引用类型等,也没有缺陷。因此,这应该为您提供正确的行为,因为确实没有理由创建 System.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();
    }
}

【讨论】:

  • @bedo 我不太明白为什么它会因对象而失败,但正如我所说,在实践中,至少应该无关紧要 ;)
【解决方案3】:

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

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

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

如果您正在寻找解决方法,您可以使用这个(我知道它很难看):

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

【讨论】:

  • 不幸的是,我也需要它来处理原语。也许我可以将对象作为特例处理,而不是在返回时进行转换。
  • 如果您尝试使用 `TryGetArrayValue(...),这甚至不会编译
  • @bedo - 我扩展了我的答案,以展示如何使用 as 关键字解决此问题。
  • 是的,这行得通。我认为将对象作为特殊情况处理是可行的方法。我只是很好奇为什么它会抛出异常。鉴于不太可能传入一个简单的对象(我不打算传入任何用于同步的对象),我可以忍受它抛出 null ref 异常并用注释记录它。
【解决方案4】:

它与动态关键字有关。如果我将类型更改为 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},这可能是尝试强制转换为对象时的问题。如果您查看我发布的这个版本,您会得到一种对象并且没有错误。

【讨论】:

  • 是的,我有一个使用动态的用例,因为我想做这样的事情:double a = 10923049; int v = TUtils.TryGetArrayValue(new object[] { a }, 0);使用动态帮助我从不同的兼容类型执行转换。否则会抛出异常。
  • @bedo 使用名为 TryGetArrayValue 的方法,我希望它应该能够失败。看到你的意图只是让我畏缩!虽然我是静态类型和编译类型检查的忠实粉丝。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-11
  • 1970-01-01
  • 2019-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多