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 Char7 |
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
xis an integral value-type (e.g.int/Int32) andyis 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
yis a type-name (instead of a value name) then there is a difference: the firstifstatement 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 nullis 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
notnegation 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
Tis 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 theisoperator, 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 unlessTwas 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
cscsource-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
constvalues,enummembers, 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 readonlyfields 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 returntruewhenXis derived fromy‘s type (as they are different types), butx is not Yis actuallyfalsebecausexisY(becausexis an instance of a subclass ofY). When usingTypeit’s better to do something liketypeof(X).IsSubclassOf(y.GetType()), or the even loosery.GetType().IsAssignableFrom(typeof(X)).- Though in this case, as
Charis 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