Is there a difference between "!=" and "is not" in C#?
Solution 1:
Comparison 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 | 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 |
Common examples:
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 |
To answer the OP's question directly and specifically:
if( x != y ) { }
// vs:
if( x is not y ) { }
-
If
x
is an integral value-type (e.g.int
/Int32
) andy
is aconst-expression
(e.g.const int y = 123;
) then no, there is no difference, and both statements result in the same .NET MSIL bytecode being generated (both with and without compiler optimizations enabled): -
If
y
is a type-name (instead of a value name) then there is a difference: the firstif
statement is invalid and won't compile, and theif( x is not y )
statement is a type pattern match instead of a constant pattern match.
Footnotes:
-
"Constant Pattern": "When the input value is not an open type, the constant expression is implicitly converted to the type of the matched expression".
-
x is not null
is more analogous to!(x == null)
thanx != null
. -
C# 7.0 introduced some limited forms of constant-pattern matching, which was further expanded by C# 8.0, but it wasn't until C# 9.0 that the
not
negation operator (or is it a modifier?) was added. -
Given a non-constrained generic method, like so:
void Foo<T>( T x ) { if( x == null ) { DoSomething(); } DoSomethingElse(); }
...when the JIT instantiates the above generic method (i.e.: monomorphization) when
T
is a value-type (struct
) then the entireif( x == null ) { DoSomething(); }
statement (and its block contents) will be removed by the JIT compiler ("elision"), this is because a value-tupe can never be equal tonull
. While you'd expect that to be handled by any optimizing compiler, I understand that the .NET JIT has specially hardcoded rules for that particular scenario.- Curiously in earlier versions of C# (e.g. 7.0) the elision rule only applied to the
==
and!=
operators, but not theis
operator, so whileif( x == null ) { DoSomething(); }
would be elided, the statementif( x is null ) { DoSometing(); }
would not, and in fact you would get a compiler error unlessT
was constrained towhere T : class
. Since C# 8.0 this seems to now be allowed for unconstrained generic types.
- Curiously in earlier versions of C# (e.g. 7.0) the elision rule only applied to the
-
Surprisingly I couldn't find an authoritative source on this (as the published C# specs are now significantly outdated; and I don't want to go through the
csc
source-code to find out either).- If neither the C# compiler or JIT do apply impossible-branch-elision in generic code with Constant-pattern expressions then I think it might simply because it's too hard to do at-present.
-
Note that a constant-expression does not mean a literal-expression: you can use named
const
values,enum
members, and so on, even non-trivial raw expressions provided all sub-expressions are also constant-expressions.- I'm curious if there's any cases where
static readonly
fields could be used though.
- I'm curious if there's any cases where
-
Note that in the case of
typeof(X) != y.GetType()
, this expression will returntrue
whenX
is derived fromy
's type (as they are different types), butx is not Y
is actuallyfalse
becausex
isY
(becausex
is an instance of a subclass ofY
). When usingType
it's better to do something liketypeof(X).IsSubclassOf(y.GetType())
, or the even loosery.GetType().IsAssignableFrom(typeof(X))
.- Though in this case, as
Char
is a struct and so cannot participate in a type-hierarchy, so doing!x.IsSubclassOf(typeof(Char))
would just be silly.
- Though in this case, as
Solution 2:
An additional difference to the ones listed in the excellent accepted answer is that (since C# 7.0), is
between two NaN values is a pattern that matches, because x.Equals(y)
is true
when both x
and y
are NaN, and a NaN value does not have an integral type. Therefore, is not
between two NaN values returns that the pattern is not a match.
However, C# follows IEEE floating-point and C by 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.