C#中的“!=”和"is not"有区别吗?

Is there a difference between "!=" and "is not" in C#?

这是:

if(x != y)
{

}

与此不同:

if (x is not y)
{

}

或者说这两个条件没有区别?

比较table:

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 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)并且yconst-expression(例如 const int y = 123;) 然后 no,没有区别,两个语句都会生成相同的 .NET MSIL 字节码(启用和不启用编译器优化):

  • 如果 y 是类型名称(而不是值名称),那么 一个区别:第一个 if 语句无效且无法编译,并且 if( x is not y ) 语句是 类型模式 匹配而不是 常量模式 匹配。


脚注:

  1. "Constant Pattern": "当输入值不是开放类型时,将常量表达式隐式转换为匹配表达式的类型"。

  2. x is not nullx != null.

    更类似于 !(x == null)
  3. C# 7.0 引入了一些有限形式的 constant-pattern 匹配,C# 8.0 对其进行了进一步扩展,但直到 C# 9.0 才not 添加了否定运算符(或者它是修饰符?)。

  4. 给定一个不受约束的泛型方法,像这样:

    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(); } 将被省略,语句 if( x is null ) { DoSometing(); } 而不是 ,事实上,除非 T 被限制为 where T : class,否则您将得到一个编译器错误。从 C# 8.0 开始,现在似乎允许不受约束的泛型类型。
  5. 令人惊讶的是,我找不到这方面的权威来源(因为已发布的 C# 规范现在已经过时了;而且我不想查看 csc 源代码找出其中一个)。

    • 如果 C# 编译器或 JIT 都没有在具有 常量模式 表达式的泛型代码中应用不可能的分支省略,那么我 认为可能只是因为目前做起来太难了。
  6. 请注意,常量表达式并不意味着文字表达式:您可以使用命名const 值,enum 成员,等等,即使是非平凡的原始表达式,所有子表达式也是 constant-expressions.

    • 我很好奇在某些情况下是否可以使用 static readonly 字段。
  7. 请注意,在 typeof(X) != y.GetType() 的情况下,当 X 派生自 y 时,此表达式将 return true' s 类型(因为它们是不同的类型),但 x is not Y 实际上是 false 因为 x Y (因为 xY 的子类的一个实例)。使用 Type 时,最好使用 typeof(X).IsSubclassOf(y.GetType()) 或更宽松的 y.GetType().IsAssignableFrom(typeof(X)).

    • 虽然在这种情况下,由于 Char 是一个结构,因此不能参与类型层次结构,所以 !x.IsSubclassOf(typeof(Char)) 只会很愚蠢。

与优秀接受的答案中列出的另一个区别是(自 C# 7.0 起),两个 NaN 值之间的 is 是匹配的模式,因为 x.Equals(y) is true when both x and y are NaN, 和 NaN 值没有整数类型。因此,is not 两个 NaN 值之间 returns 表示模式不匹配。

但是,C# 遵循 IEEE 浮点和 C specifying that a != comparison between two NaN values is true and an == comparison between them is false. This was mainly because the Intel 8087 floating-point co-processor back in 1980 had no other way to test for NaN.

Nan 和 null 是变量可以包含但没有值的属性。相等性检查需要实际值来确定相等性。毕竟谁都不知道莎莉和彼得有多少个苹果,问莎莉和彼得有多少苹果是没有意义的。

有时您想检查一个变量是否有 属性 没有值。基本的相等性检查对此是不够的。那就是当 is / is not 运算符有用的时候。可以说 != 是一个值检查 where is / is not a 属性 check.