在我看来,is
运算符有点不一致。
bool Test()
{
// Returns false, but should return true.
return null is string;
}
人们期望 null
值属于任何引用(或可为空)类型。确实,C# 语言规范说明了一些支持这一假设的内容,例如(6.1.6 隐式引用转换):
隐式引用转换是: ... • 从空文字到任何引用类型。
is
运算符的描述(7.10.10 is 运算符)首先说,当存在从 E
到 T
的引用转换时,表达式 (E is T)
将导致 true,但随后作者继续明确排除 E
是 null
文字或具有 null
值的情况。
他们为什么这样做?在我看来,这似乎违反直觉。
null typeof Object
中也返回 false
null is string
是 true
,这意味着 null
是 string
。还假设 null is Nullable<int>
是 true
,这意味着 null
是 Nullable<int>
。现在,回答这个问题:null
是什么类型的?
这个问题是subject of my blog on May 30th 2013。谢谢你的好问题!
你盯着空荡荡的车道。
有人问你“你的车道能停一辆本田思域吗?”
是的。是的,它可以。
有人指给你第二条车道。它也是空的。他们问“我车道的当前内容是否适合您的车道?”
是的,很明显。两条车道都是空的!很明显,一个的内容可以适合另一个,因为首先没有任何一个的内容。
有人问你“你的车道上有本田思域吗?”
不,不是的。
您认为 is
运算符回答了第二个问题:给定这个值,它是否适合该类型的变量?空引用是否适合这种类型的变量?是的,它确实。
这不是 is
运算符回答的问题。 is
运算符回答的问题是第三个问题。 y is X
不问“y
是 X
类型变量的合法值吗?”它问“y
是对 y
类型对象的有效引用吗? 5}?" 由于空引用不是对 any 类型的 any 对象的有效引用,因此答案是“否”。那条车道是空的;它不包含本田思域。
另一种看待它的方式是,y is X
回答了“如果我说 y as X
,我会得到非空结果吗?如果 y 为空,显然答案是否定的!
要更深入地了解您的问题:
期望空值属于任何引用(或可为空)类型
有人会隐含地假设一个类型是一组值,并且值 y 与类型 X 的变量的赋值兼容性只不过是检查 y 是否是集合 x 的成员。
尽管这是查看类型的一种极为常见的方式,但这并不是查看类型的唯一方式,也不是 C# 查看类型的方式。空引用是 C# 中无类型的成员;赋值兼容性不仅仅是检查一个集合是否包含一个值。仅仅因为空引用与引用类型 X 的变量的赋值兼容并不意味着 null 是 X 类型的成员。“赋值兼容”关系和“是类型的成员”关系显然有很多重叠,但它们在 CLR 中并不相同。
如果您对类型理论感兴趣,请查看我最近关于该主题的文章:
What is this thing you call a "type"? Part one
What is this thing you call a "type"? Part Two
我认为 null is string
返回 false 非常直观。 Null 没有任何意义,而且它绝对不是字符串。所以它应该返回false。虽然这是语言设计者做出的选择,但当您考虑 null 的实际含义时,它是一个非常直观的选择。
null
a string”来问它,答案是否定的,它不是,这与“is null
assignable to string
”不同
null is string
非常直观,如果您不太熟悉 C# 中空引用的工作原理,(string)null is string
可能需要暂停一下。
null
文字可以分配给 any 引用类型。它本身不是一种类型。它是一个特殊的 literal,表示一个空引用。
如果传入 null
时 is
会返回 true
,那么您可以使用 null
文字做什么?什么都没有 - 它是 null
。除了令人困惑的事情之外,它返回 true
有什么意义?
无论如何-就直观程度而言,请阅读英文代码并告诉我:
null is string;
当我看到它时,它似乎在问问题 is "nothing" a string?
。我的直觉告诉我,不,它不是——它是 nothing
。
(null is string)
会返回 flash 呢?
(null is object)
表达式应该准确地检查这一点。
is
运算符检查是否存在引用(我们这里不需要其他类型的转换)转换; (2) null
可以转换为任何引用类型。因此 (null is T)
,其中 T 是一个引用类型,应该返回 true。但是不,这里我们有一个明确的例外,这让我感到困惑。
http://msdn.microsoft.com/en-us/library/scekt9xw%28v=vs.71%29.aspx
如果满足以下两个条件,则 is 表达式的计算结果为 true:表达式不为空。表达式可以转换为类型。也就是说,(type (expression) 形式的强制转换表达式将在不抛出异常的情况下完成。有关更多信息,请参阅 7.6.6 强制转换表达式。
实际上,使用“null is T == false”可以节省我输入额外的代码:
而不是不得不说
if (X != null && X is Foo) {}
我只能说
if (X is Foo) {}
并完成它。
空值
我从你的问题中引用了这一点,因为它似乎触及了问题的核心。 null
不是一个值 - 它是一个值的缺失。对我来说,is
的目的似乎是回答这个问题:
如果我将 E 转换为 T,我会成功获得 T 吗?
现在,虽然您可以将 null
转换为 T
没有错误,但在这样做之后,您不“有一个 T
“——你还是一无所有。因此,null
并非“是”T
,因此 is
返回 false。
在 Java 中,有一个操作符可以做完全相同的事情,但它的名称要长得多:instanceof
。 null instanceof String
返回 false 非常直观,因为 null 不是任何事物的实例,更不用说 String
。因此,当使用 null
时,Java 的版本更加直观。
但是,当被要求查看整个层次结构时,这两个运算符都返回 true。例如。如果 String
的实例是 Object
。这里是 Java 不太直观(因为一个实例实际上有一个非常具体的类型),而 C# 的 is
更直观(因为每个 String
是 一个 Object
深入内部)。
底线:如果你试图用一个词来描述相当高级的逻辑,你肯定很少有人会以这种或另一种方式感到困惑。似乎大多数人都同意一个含义,而那些不同意的人则不得不调整。
y is X
不问“y
是X
类型变量的合法值吗?”它询问“y
是对X
类型对象的有效引用吗?”这就是重点!谢谢!y is int?
,那么我会问“你的车道是否包含本田思域或什么都没有?”。当您的车道空无一人时,为什么答案仍然是否定的?如果您深入了解情况,情况只会变得更糟,因为问题变成了“您的车道上是否有本田思域或标有‘这里没有本田思域’的标志?”。即使您的车道上有上述标志,答案仍然是否定的。