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 开始。还有 orand 关键字。 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 Char7
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 具有针对该特定场景的特殊硬编码规则。

    奇怪的是,在 C# 的早期版本(例如 7.0)中,省略规则仅适用于 ==!= 运算符,但不适用于 is 运算符,因此虽然 if( x == null ) DoSomething(); 将被省略,但声明 @987654378 @ 不会不会,事实上你会得到一个编译器错误,除非T 被限制为where T : class。从 C# 8.0 开始,这似乎允许用于不受约束的泛型类型。

    令人惊讶的是,我找不到这方面的权威来源(因为已发布的 C# 规范现在已经明显过时了;我也不想通过 csc 源代码来查找)。

    如果 C# 编译器或 JIT 都没有在具有 Constant-pattern 表达式的通用代码中应用不可能分支省略,那么我认为这可能只是因为它太难了目前做。

    请注意,constant-expression 并不意味着 literal-expression:您可以使用命名的 const 值、enum 成员等等,甚至非平凡的原始表达式提供的所有子表达式也是 constant-expressions

    我很好奇是否有任何情况可以使用static readonly 字段。

    请注意,在typeof(X) != y.GetType() 的情况下,当X 派生自y 的类型(因为它们是不同的类型)时,此表达式将返回true,但x is not Y 实际上是false因为x Y(因为xY 的子类的一个实例)。使用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 如果objAobjB 相同,或者两者都是null。”因此,null == nullnull is null 给出相同的结果。 也就是说,除非!= 以令人惊讶的方式超载,否则没有差异。 顺便说一下,the spec says if (expr is Type v) 是作为显式 null 检查的简洁替代品而创建的。因此,MS 的家居风格比v != nullv is not null 更受欢迎。 顺便说一句,反对票不是来自我。 “您不能将值与 NaN 和 NULL 进行比较”的理念是错误的。在那里,我说了算。它不会为语言增加任何价值,并且会恶化 dev-ex。我会反驳说显然有可能设计一种具有清晰、直接和逻辑一致规则的语言,允许使用相等运算符对空值进行有意义的比较(实际上:C# 做得很好!就像 Java , 和 C++ 等),而 ISO SQL 严格规定 x = NULLUNKNOWN ,这对每个人来说都是一个重要的gotcha,从初学者到专家。 SQL 中的IS NULL 只是一个狗圈。

以上是关于C# 中的“!=”和“不是”之间有区别吗?的主要内容,如果未能解决你的问题,请参考以下文章

C# 中的 Icompare 接口和 IComparable 接口有啥区别??

在C#中long与int64有啥区别吗

java中的并发和并行之间有区别吗?

Objective-C 中的 bool、Boolean 和 BOOL 之间有啥区别吗?

c 和 c++ 中的指针之间有啥大的区别吗? [关闭]

PyTorch 中的 index_select 和 tensor[sequence] 之间有啥区别吗?