C# 中的“!=”和“不是”之间有区别吗?
Posted
技术标签:
【中文标题】C# 中的“!=”和“不是”之间有区别吗?【英文标题】:Is there a difference between "!=" and "is not" in C#? 【发布时间】:2021-12-28 19:43:40 【问题描述】:这是:
if(x != y)
与此不同:
if (x is not y)
或者这两种情况没有区别?
【问题讨论】:
@JohnWu:是的,从 C# 9 开始。还有or
和 and
关键字。 docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
@JohnWu C# 在将近 2 年前的圣诞节做的时候离 VB 太近了;最终发现了一个叫做 C#ViB-19 的东西——自从
@CaiusJard 我知道乍一看似乎很愚蠢,但现在我喜欢它:能够做到 x is 1 or 2 or 3
比做到 ( x == 1 || x == 2 || x == 3 )
好很多 和作为奖励:当x
是一个表达式而不是一个值时,is
运算符只计算x
一次,而( x == 1 || x == 2 || x == 3 )
将导致x
的3 次计算。跨度>
@Dai - 我真的对编译器了解不多(如果有的话),但在像( x == 1 || x == 2 || x ==3 )
这样简单的情况下,编译器无法将其优化为单一评估然后以与x is 1 or 2 or 3
相同的方式进行比较?老实说,我一秒钟都没有怀疑你——我只是要求自己接受教育。
@Spratty 对于简单字段或局部变量,通常没有区别,但对于其他类型的表达式(例如,计算属性),它不能总是安全地将第一种情况优化为单个评估因为评估 x
可能会产生副作用,并且编译器无法知道代码是否打算在每次比较后重新评估表达式。
【参考方案1】:
对比表:
Operator | != |
is not |
---|---|---|
Original purpose | Value inequality | Negated pattern matching |
Can perform value inequality | Yes | Yes |
Can perform negated pattern matching | No | Yes |
Can invoke implicit operator on left-hand operand |
Yes | No |
Can invoke implicit operator on right-hand operand(s) |
Yes | Yes1 |
Is its own operator | Yes | No2 |
Overloadable | Yes | No |
Since | C# 1.0 | C# 9.03 |
Value-type null-comparison branch elision4 | Yes | No[Citation needed]5 |
Impossible comparisons | Error | Warning |
Left operand | Any expression | Any expression |
Right operand(s) | Any expression | Only constant expressions6 |
Syntax | <any-expr> != <any-expr> |
<any-expr> is [not] <const-expr> [or|and <const-expr>]* and more
|
常见例子:
Example | != |
is not |
---|---|---|
Not null | x != null |
x is not null |
Value inequality example | x != 'a' |
x is not 'a' |
Runtime type (mis)match | x.GetType() != typeof(Char) |
x is not Char 7
|
SQL x NOT IN ( 1, 2, 3 )
|
x != 1 && x != 2 && x != 3 |
x is not 1 or 2 or 3 |
直接具体地回答 OP 的问题:
if( x != y )
// vs:
if( x is not y )
如果x
是一个整数值类型(例如int
/Int32
)并且y
是一个const-expression
(例如const int y = 123;
)那么否,有没有区别,两个语句都会生成相同的 .NET MSIL 字节码(启用和不启用编译器优化):
如果 y
是类型名称(而不是值名称),那么 不同:第一个 if
语句无效且无法编译,而 @987654364 @ 语句是 type 模式 匹配而不是 constant 模式 匹配。
脚注:
"Constant Pattern": "当输入值不是开放类型时,常量表达式隐式转换为匹配表达式的类型"。
x is not null
更类似于!(x == null)
而不是x != null
。
C# 7.0 引入了一些有限形式的 constant-pattern 匹配,C# 8.0 对此进行了进一步扩展,但直到 C# 9.0 才出现 not
否定运算符(或者它是修饰符?)。
给定一个不受约束的泛型方法,如下所示:
void Foo<T>( T x )
if( x == null ) DoSomething();
DoSomethingElse();
...当 JIT 实例化上述通用方法(即:monomorphization)时,当 T
是值类型(struct
)时,整个 if( x == null ) DoSomething();
语句(及其块内容)将被 JIT 编译器删除(“省略”),这是因为值元组永远不会等于 null
。虽然您希望任何优化编译器都能处理这种情况,但我知道 .NET JIT 具有针对该特定场景的特殊硬编码规则。
==
和 !=
运算符,但不适用于 is
运算符,因此虽然 if( x == null ) DoSomething();
将被省略,但声明 @987654378 @ 不会不会,事实上你会得到一个编译器错误,除非T
被限制为where T : class
。从 C# 8.0 开始,这似乎允许用于不受约束的泛型类型。
令人惊讶的是,我找不到这方面的权威来源(因为已发布的 C# 规范现在已经明显过时了;我也不想通过 csc
源代码来查找)。
请注意,constant-expression 并不意味着 literal-expression:您可以使用命名的 const
值、enum
成员等等,甚至非平凡的原始表达式提供的所有子表达式也是 constant-expressions。
static readonly
字段。
请注意,在typeof(X) != y.GetType()
的情况下,当X
派生自y
的类型(因为它们是不同的类型)时,此表达式将返回true
,但x is not Y
实际上是false
因为x
是 Y
(因为x
是Y
的子类的一个实例)。使用Type
时,最好使用typeof(X).IsSubclassOf(y.GetType())
之类的东西,或者更宽松的y.GetType().IsAssignableFrom(typeof(X))
。
Char
是一个结构,因此不能参与类型层次结构,所以这样做!x.IsSubclassOf(typeof(Char))
只是愚蠢的。
【讨论】:
第 8 行中的“elison”一词是什么意思? @dennis_vok 我猜他指的是en.wikipedia.org/wiki/Copy_elision。编辑排队 @EtsitpabNioliv 在这种情况下,它不是复制省略,而是编译器优化以删除只有一个结果的比较。例如。如果x
不可为空,则x != null
始终可以优化为true
。
感谢@cg909 的澄清,这绝对是一个小众术语,我将wiki 链接换成了ericlippert.com/2013/01/24/…
截图的软件是什么?【参考方案2】:
与优秀公认答案中列出的另一个区别是(自 C# 7.0 起),两个 NaN 值之间的 is
是匹配的模式,因为 x.Equals(y)
is true
when both x
and y
are NaN, 和 NaN 值没有整数类型.因此,两个 NaN 值之间的is not
返回模式不匹配。
但是,C# 在 IEEE 浮点和 C 之后遵循 specifying that 两个 NaN 值之间的 !=
比较是 true
并且它们之间的 ==
比较是 false
。这主要是因为the Intel 8087 floating-point co-processor back in 1980 had no other way to test for NaN.
【讨论】:
两个不是数字的值毕竟不能相等。计算机肯定会使用这种令人愉快的逻辑。为什么生活中更多的事情不能这样运作? @NeilMeyer “爱因斯坦认为必须对自然进行简化的解释,因为上帝不是反复无常或任意的。没有这样的信念能让软件工程师感到安慰。” ——弗雷德·布鲁克斯 英特尔 8087 确实有其他方法可以测试 NaN,例如 FXAM instruction。主要原因是像 C 这样的语言缺乏标准化的 isNaN 原语。 @nwellnhof 您的链接似乎已损坏,但感谢您的更正。我会进一步研究并酌情进行编辑。【参考方案3】:Nan 和 null 是变量可以包含的没有值的属性。相等性检查需要一个实际值来确定相等性。毕竟,在没人知道莎莉和彼得有多少苹果的情况下,关于莎莉和彼得是否有相同数量的苹果的问题毫无意义。
有时你想检查一个变量是否有一个没有值的属性。基本的平等检查是不够的。那就是当 is / is not 运算符有用时。可以说 != 是值检查,而 / 不是属性检查。
【讨论】:
虽然null
原则上与 NaN 相似,但 null
引用上的 is
和 ==
之间没有差异。 null == null
评估为真。两个引用类型之间的模式匹配被指定为与Equals(Object)
一致,并且“对Equals(Object)
方法的调用等同于对ReferenceEquals
方法的调用。”这又被指定为“true
如果objA
与objB
相同,或者两者都是null
。”因此,null == null
和 null is null
给出相同的结果。
也就是说,除非!=
以令人惊讶的方式超载,否则没有差异。
顺便说一下,the spec says if (expr is Type v)
是作为显式 null
检查的简洁替代品而创建的。因此,MS 的家居风格比v != null
或v is not null
更受欢迎。
顺便说一句,反对票不是来自我。
“您不能将值与 NaN 和 NULL 进行比较”的理念是错误的。在那里,我说了算。它不会为语言增加任何价值,并且会恶化 dev-ex。我会反驳说显然有可能设计一种具有清晰、直接和逻辑一致规则的语言,允许使用相等运算符对空值进行有意义的比较(实际上:C# 做得很好!就像 Java , 和 C++ 等),而 ISO SQL 严格规定 x = NULL
是 UNKNOWN
,这对每个人来说都是一个重要的gotcha,从初学者到专家。 SQL 中的IS NULL
只是一个狗圈。以上是关于C# 中的“!=”和“不是”之间有区别吗?的主要内容,如果未能解决你的问题,请参考以下文章
C# 中的 Icompare 接口和 IComparable 接口有啥区别??