【问题标题】:Casting null doesn't compile强制转换 null 不会编译
【发布时间】:2012-01-25 18:43:17
【问题描述】:

在工作中不小心写了下面这行代码:

string x = (object) null; 
// It was var x = (object)null and I changed from var to string instead of 
// object x = null;

这给了我一个类似这样的编译错误:Can't cast source type object to target type string

为什么?无论类型是什么,null 不只是一堆指向“无处”内存地址的零吗?

【问题讨论】:

  • 你为什么不分配string x = string.empty;
  • 为什么要投? string x = null..
  • 如果你刚刚说string x = null;,你就不会遇到这个问题。但这不是你说的。你说 string x = someObj; 其中 someObj 是一个 object 恰好是一个空值。就编译器而言,您有一个试图分配给字符串的对象引用,这是不合法的。
  • @harold。你是对的,这是错误的,但我仍然很感兴趣。
  • 您需要显式转换才能将object 引用向下转换为string。因此使用string x = (string)(object)null;。正确的? ;-)

标签: c# .net compiler-construction null


【解决方案1】:

问题不在于null 的转换,而是object 不能分配给string。这很好用

string x = (string)null;

如果您删除演员表 (string x = null),这会起作用的原因在 C# 语言规范的第 2.4.4.6 节中进行了说明

null-literal 可以隐式转换为引用类型或可空类型

在您引入演员表 ((object)null) 的那一刻,您将不再拥有 null 文字。相反,您有一个object 类型的值。本质上和

没有区别
object temp = null;
string x = temp;

【讨论】:

  • 我知道如何解决它。我在问空对象和空字符串有什么区别?
  • 您仍然认为它是“null”,但您已经说过它是一种特殊类型的对象。
  • @gdoron null object 和 null string 有什么区别? 它们的类型,这让世界变得与众不同。类似于问(float)3(int)3 有何不同。
  • @gdoron 我更新了我的答案,以便在这里更多地解释语义
  • @Lagrangian 这是 C# 3.5 语言规范的第 2.4.4.6 节。可以从此页面上的链接下载它msdn.microsoft.com/en-us/library/ms228593.aspx
【解决方案2】:

这里的问题基本上是“为什么编译器不考虑它知道分配的值是一个已知为空的常量引用这一事实?”

答案是:为什么要这样做?考虑到这些信息有什么令人信服的好处?您故意说“我希望将此表达式视为对象类型”,并且您不能将对象类型的值分配给字符串类型的变量。在这种情况下允许这样做有什么好处?

在我看来,代码很可能是一个错误;当然编译器应该告诉你而不是允许它。

【讨论】:

  • 谢谢,知道了。但是一个空的object 和一个空的string 指的是同一个内存地址是真的吗?
  • @gdoron: 内存地址? 你称之为“内存地址”的东西是什么? null reference 是一个reference。在实践中,当然,引用通常被实现为地址。但不一定;例如,引用可以是垃圾收集器维护的大表的句柄。不要将引用视为地址;将它们视为引用某物的抽象概念。
  • @gdoron:现在我们已经解决了这个问题:空对象和空字符串是否指同一个东西?不,当然不是。 他们不指任何东西。这样想。我有一张标有“最喜欢的冰淇淋”的纸。它是空白的。你有一张标有“最喜欢的电影”的纸。它也是空白的。那两张纸“指的是同一件事”吗?当然不是。它们都没有指代任何东西,因此它们都指代的相同的东西
  • 或者,更好的是:我的论文说“没有,因为我没有最喜欢的冰淇淋”,​​而你的说“没有,我没有最喜欢的电影”。现在这两篇论文的内容是指“同一件事”吗?试着向我展示这两篇论文所指的事物!根据定义,“无”不是事物
  • 越看你的回答和博文,我觉得你真的应该写一本关于生命意义的书。 (或者null的意思...)
【解决方案3】:

无论类型是什么,null 不只是一堆指向“无处”内存地址的零吗?

这就是 C 或 C++ 等弱类型语言的全部内容。

在 C# 中,引用的类型是其标识的组成部分。 (string)null(object)null 不同,因为一个是 string,一个是 object

此外,在 C# 中,null 并没有真正的数字等价物。 C# 中的引用与指针不同,从语义上讲,它们没有关联的内存地址。 null 只是表示引用不指向对象,null 引用的内部表示是一个实现细节。

【讨论】:

    【解决方案4】:

    它必须是可赋值的——即使它看起来是“相同的 null”并且这无关紧要,编译器仍然支持该类型。这样做的一个好处是解决重载:

    void Foo(object bar) { ... }
    void Foo(string bar) { ... }
    
    Foo((object)null); // will call the former
    Foo((string)null); // will call the latter
    

    【讨论】:

    • 一个有趣的答案!但是让我们假设 (object)null 可以隐式转换为字符串。重载解析算法仍然会选择对象版本,因为那是 exact 类型匹配。重载解析算法总是说,在尝试确定两种方法中的哪一种更好时,标识转换优于任何其他转换。
    • 我应该更多地访问您的博客(并重读 Jon Skeet)。使用 C# 8 年后,我仍然不得不在 VS 中检查上述代码,看看它是否真的有效。 ;-)
    【解决方案5】:

    也许object x = (string) null; 可能有用,但你为什么会呢?

    因为对象可以保存字符串,但字符串不能保存对象

    字符串是从对象继承的,而不是相反。

    【讨论】:

      【解决方案6】:

      您可以将 null 分配给任何引用类型的变量,而无需强制转换:

      String x = null;
      

      【讨论】:

        【解决方案7】:

        每个类都有一个定义列表:

        Constructors
        Destructors
        Fields
        Methods
        Properties
        Indexers
        Delegates
        Events
        Nested Classes
        

        Object 是一个泛型类型,因为每个类都继承自它,但不是相反。这些定义对于每个类都不相同,因此您的编译器可以决定您可以相互分配/转换哪些类型。

        然后你做:string x = (object)null;

        编译器并不关心您首先尝试分配给您的 x 的值,但它会检查字符串的类型定义,只是不让您射击自己的腿,并生成错误,因为它是类型不匹配。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-12
          • 2011-05-28
          相关资源
          最近更新 更多