2012-03-29 41 views
61

我有以下功能:为什么将类型对象的动态转换为抛出空引用异常的对象?

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

除了“对象”以外,我放入其他任何类型,它工作得很好。
任何想法这是什么,以及它为什么抛出异常?

编辑: 为什么我用动态的原因是转换类型时,例如为了避免异常:

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

检查铸造前是否有“boxed”为空。 – 2012-03-29 18:56:42

+5

这可能是'dynamic'实现的一个错误。现在检查4.5中的这个repros。 – dlev 2012-03-29 19:01:12

+1

Repro - 它的确看起来像一个bug,它是如何处理'dynamic'的 - 这个问题是有效的 – BrokenGlass 2012-03-29 19:02:00

回答

41

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

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

感谢您将此引入我们的注意。如果您觉得entering a Connect issue需要跟踪它,请随时这样做,并请包括指向此StackOverflow问题的链接。如果你不这样做,没问题;测试团队会以任何方式了解它。

+1

我可以获得发现错误的荣誉提及吗? :D – bedo 2012-03-29 19:54:44

+28

@bedo:绝对。十点给格兰芬多! – 2012-03-29 19:55:22

+0

我刚刚标记了另一个问题作为这个副本。是否向Connect报告了一个错误?如果有,可以跟踪状态的链接? – 2016-05-24 19:04:11

2

它有事情做与动态关键字。如果我改变类型为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

是的,我有一个使用动态的用例,因为我想要做这样的事情: double a = 10923049; int v = TUtils.TryGetArrayValue (new object [] {a},0); 使用动态帮助我执行不同兼容类型的强制转换。否则它会抛出一个异常。 – bedo 2012-03-29 19:10:15

+0

@bedo与一个名为TryGetArrayValue的方法,我希望它应该能够失败。看到你的意图只是让我感到害怕!虽然我是静态打字和编译类型检查的忠实粉丝。 – Jetti 2012-03-29 19:16:57

6

这实在是很奇怪的行为,在dynamic的执行过程中的确看起来像一个bug。我发现,这个变化不会抛出异常,事实上返回对象:

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

不幸的是我需要它为原语工作。也许我可以把对象当作特殊情况处理,而不是在我返回时进行投射。 – bedo 2012-03-29 19:18:12

+0

如果您尝试使用TryGetArrayValue ( – Jetti 2012-03-29 19:18:13

+0

@bedo),这甚至不会编译 - 我扩展我的答案以显示如何使用'as'关键字解决此问题。 – 2012-03-29 19:28:55

14

这是如何工作的动力问题 - 运行时粘结剂与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(); 
    } 
} 
+0

太好了,谢谢你的解释! – bedo 2012-03-29 19:29:14

+0

@bedo我不太明白为什么它会以对象失败,但正如我所说的,在实践中,至少应该没有关系;) – 2012-03-29 19:33:52

相关问题