为什么is运算符在给定null时返回false?

在我看来,运算符有点不一致。

 bool Test() { // Returns false, but should return true. return null is string; } 

人们期望null值属于任何引用(或可空)types。 事实上,C#语言规范说明了一些支持这个假设的东西,例如(6.1.6隐式引用转换):

隐含的参考转换是:

•从null文字到任何引用types。

is运算符的描述(7.10.10 is运算符)首先表示当ET的引用转换存在时,expression式(E is T)将导致真实,但是作者通过明确排除该情况继续当Enull文字或有一个null值。

他们为什么这样做? 对我来说,这似乎违反直觉。

这个问题是我的博客在2013年5月30日的主题 。 感谢您的好问题!


你正盯着一个空的车道。

有人问你:“你的车道可以放一辆本田思域?

是。 是的,它可以。

有人在第二个车道指向你。 它也是空的。 他们问:“我的车道上目前的内容是否适合你的车道?”

是的,显然。 两车道都是空的! 所以很明显,一个人的内容可以放在另一个人的内部,因为没有任何内容。

有人问你:“你的车道是否有本田思域?

不,不是的。

你认为is运算符回答第二个问题: 给定这个值,它是否适合于这种types的variables? 空引用是否适合这种types的variables? 是的,它确实。

这不是运营商回答的问题。 运营商回答的问题是第三个问题。 y is X不问“ 是一个Xtypes的variables的合法值吗? ”它问“ 是一个Xtypes的对象的有效引用吗? ”由于空引用不是任何对象的有效引用types,答案是“否”。 那条车道是空的 它不包含本田思域。

另一种看待它的方法是y is X回答“如果我把y as X表示y as X ,我会得到一个非空的结果吗?如果y为空,那么答案显然是否定的!


在你的问题上稍微深入一点:

人们期望空值属于任何引用(或可空)types

我们可以隐式地假设一个types是一组值 ,并且值y和variablestypesX的赋值兼容性不过是检查y是否是set x的成员

虽然这是查看types的一种非常常见的方式,但这不是查看types的唯一方式,也不是C#查看types的方式。 空引用是C#中没有types的成员; 赋值兼容性不仅仅是检查一个集合是否包含一个值。 仅仅因为一个空引用是与引用typesX的variables兼容的 ,并不意味着null是typesX的成员。“赋值兼容”关系和“是types成员”关系显然有很多重叠,但在CLR中它们并不相同。

如果关于types理论的思考关注你,请查看我最近关于这个主题的文章:

你称之为“types”是什么东西? 第一部分

你称之为“types”是什么东西? 第二部分

null文字可以被分配给任何引用types。 它本身不是一种types。 它是一个特殊的文字代表一个空引用。

在传递null的情况下返回true的情况下,你能用null字符做些什么? 没有 – 它是null 。 除了混淆的事情之外,什么才能回归true呢?


无论如何 – 就直觉来讲,用英文阅读代码告诉我:

 null is string; 

当我看到这个问题的时候,似乎是在问这个问题is "nothing" a string? 。 我的直觉告诉我,不,不是 – nothing

我认为null is string返回false是非常直观的。 Null意味着什么,它绝对不是一个string。 所以它应该返回false。 虽然这是语言devise者所做的select,但是当您考虑null的真实世界含义时,这是一个非常直观的select。

http://msdn.microsoft.com/en-us/library/scekt9xw%28v=vs.71%29.aspx

如果满足以下两个条件,则expression式的计算结果为true:

  • expression式不为空。
  • expression式可以转换为types。 也就是说,表单(expression式)的强制转换expression式将会在不抛出exception的情况下完成,更多信息请参见7.6.6 Castexpression式。

作为一个实际的问题,“null是T ==假”节省我input额外的代码:

而不必说

 if (X != null && X is Foo) {} 

我可以说

 if (X is Foo) {} 

并完成它。

null

我已经从你的问题中引用了这个,因为它似乎成了问题的核心。 null 不是一个值 – 这是没有价值。 对我来说,目的似乎是回答这个问题:

如果我把E投给T ,我会成功拿到T吗?

现在,虽然你可以 没有错误地T施加到T ,但是在这样做之后你不会 “有T ” – 你什么都没有。 所以不是null “是”一个T ,所以is返回false。

在Java中,有一个操作符完全相同,但名称更长: instanceof 。 这非常直观, null instanceof String返回false,因为null不是任何东西的实例,更不用说String 。 所以,使用null ,Java的版本更直观一些。

但是,当要求查看整个层次结构时,这两个操作符都会返回true。 例如。 如果String实例是一个Object 。 在这里,Java不那么直观(因为一个实例实际上有一个非常具体的types),而C#则更直观(因为每个String 都是内部深处的Object )。

底线:如果你试图用一个词来形容非常先进的逻辑,你肯定会有这样那样的困惑。 看来大多数人都同意了一个意思,那些不同意的人不得不调整。