In this article, we are going to explore the differences between != and Is Not operators in C#.
Let’s start.
Inequality != Operator
Before we start talking about the differences between != and Is Not operators, we have to mention both of them separately.
The inequality (!=) operator exists since C# 1.0 and it evaluates if the term to the left is different from the one to the right. Both on the left and right sides of the inequality expression, we can find two variables (or even complex expressions) having the same built-in type. We write it as x != y
and this form is equal to !(x==y)
.
Now, let’s prepare a few classes that we can use to see the inequality operator in action.
We are going to start by defining the Vehicle
class, having two properties SerialNumber
and VehicleBrand
:
public class Vehicle { public Brand VehicleBrand { get; set; } public int SerialNumber { get; set; } }
Since we need the Brand
enumeration, let’s add it as well:
public enum Brand { Ford = 1, Toyota = 2 }
To continue, we are going to create an instance of Vehicle
in the Program
class:
var vehicle = new Vehicle() { SerialNumber = 1001, VehicleBrand = Brand.Toyota };
Finally, let’s define the Car
class, derived from Vehicle
:
public class Car : Vehicle { public string? Model { get; set; } }
To show a simple use case for !=
operator, we can create the InequalityVsIsNotComparer
class with the SerialNumberComparerUsingNotEqual
method :
public class InequalityVsIsNotComparer { public const int _constantSerialNumber = 1000; public void SerialNumberComparerUsingNotEqual(Vehicle vehicle) { if (vehicle.SerialNumber != _constantSerialNumber) { Console.WriteLine("serial number != 1000"); } } }
This method takes a Vehicle
as input and it compares SerialNumber
property with a constant using the !=
operator. The two numbers are different, so invoking the method in our Program.cs
will print our message.
Is Not Operator
The is not
operator has the purpose to negate a pattern matching. A pattern-matching verifies if a run-time type of a term matches with a given type. Its syntax has the form:
<expression> is not <constant expression>
.
In this case, the right term of the expression must be constant.
That said, let’s use the same Vehicle
example, to show how this operator works:
public void SerialNumberComparerUsingIsNot(Vehicle vehicle) { if (vehicle.SerialNumber is not _constantSerialNumber) { Console.WriteLine("serial number is not 1000"); } }
In this method, we are using the same vehicle instance. So invoking this method in our Program.cs
will print:
serial number is not 1000
Differences Between != and Is Not Operators
Now, let’s try to explain the differences between these two operators. We can notice that, while inequality has its !=
operator, is not
is a combination of two operators is
and not
. is not
is more similar to !(x==y)
than x!=y
because the first one uses two operators ==
and !
.
Let’s show some situations where differences between !=
and is not
arise.
Constant Expressions
We have already seen that for the inequality operator we can have any expression both on the left and right sides. But when we use is not
, the right-side expression needs to be constant. This means that we will have a compilation error if we try to write:
public void SerialNumberComparerUsingIsNot(Vehicle vehicle) { var serialNumber = 10; if (vehicle.SerialNumber is not serialNumber) // This throws CS10150 - A constant value is expected error { Console.WriteLine("boxed serial number != 1000"); } }
This is because serialNumber
is not a constant.
We have to pay attention that a “constant expression” is not a “literal expression”. In fact:
public void BrandComparerUsingIsNot(Vehicle vehicle) { if (vehicle.VehicleBrand is not Brand.Ford) { Console.WriteLine("brand is not Ford"); } } public void ModelComparerUsingIsNot(Car car) { if (car.Model is not "Focus") { Console.WriteLine("model is not Focus"); } } public void AnotherModelComparerUsingIsNot(Car car) { const string a = "Fo"; const string b = "cus"; if (car.Model is not $"{a}{b}") { Console.WriteLine("model is not Focus"); } }
For all these methods we are using is not
correctly. The first method checks two enums, the second one checks two strings, and the third one compares car.Model
with a combination of two constant strings.
Impossible Comparisons
!=
and is not
have different ways to manage impossible comparisons:
public void ImpossibleComparer(Vehicle vehicle) { if (vehicle.SerialNumber != "test string") //Error CS0019 Operator '!=' cannot be applied to operands of type 'int' and 'string' { } if (vehicle.SerialNumber is not "test string") //Error CS0029 Cannot implicitly convert type 'string' to 'int' { } }
The SerialNumber
is of type int, and this causes two different compilation errors. We can see that !=
typically forces us to have the same built-in type on both sides of the comparison (We say typically because developers may declare operator overloads that compare different types. Whether one should or not is another matter). On the other hand, is not
can compare the terms if the left-hand type can be converted to the one on the right. If this is possible, then the expression is evaluated.
Comparison with Boxing
In this section, we are going to see how these two operators behave with boxing.
Let’s start by using the !=
operator:
public void BoxedSerialNumberComparerUsingNotEqual(Vehicle vehicle) { object boxedSerialNumber = vehicle.SerialNumber; if (boxedSerialNumber != _constantSerialNumber)//This will get CSS0019 (Operator '!=' cannot be applied to operands of type 'object' and 'int') { Console.WriteLine("boxed serial number != 1000"); } }
We have a compilation error since we are comparing an object and an int. To have exactly the same type on both sides, we can force the cast:
public void BoxedSerialNumberComparerUsingNotEqual(Vehicle vehicle) { object boxedSerialNumber = vehicle.SerialNumber; if (boxedSerialNumber != (object)_constantSerialNumber) { Console.WriteLine("boxed serial number != 1000"); } }
This doesn’t happen when we deal with is not
:
public void BoxedSerialNumberComparerUsingIsNot(Vehicle vehicle) { object boxedSerialNumber = vehicle.SerialNumber; if (boxedSerialNumber is not _constantSerialNumber) { Console.WriteLine("boxed serial number is not 1000"); } }
Inheritance Management
As we know from the inheritance paradigm, a Vehicle
is not a Car
, while a Car
is a Vehicle
. That said, if we compare these two types:
public void CarTypeComparerUsingNotEqual(Car car) { if (car.GetType() != typeof(Vehicle)) { Console.WriteLine("car != Vehicle"); } }
We are going to get an expected result:
car != Vehicle
We expected this result since the types are effectively different.
Now, let’s see what is going to happen if we create a comparison using the is not
operator:
public void CarTypeComparerUsingIsNot(Car car) { if (car is not Vehicle) { Console.WriteLine("car is not Vehicle"); } else { Console.WriteLine("car is Vehicle"); } }
In this case, is not
operator takes into consideration the inheritance between classes, and we get a different output:
car is Vehicle
Operator Overload
Another important difference concerns the possibility to overload operators ==
and !=
. In fact, we know that in C# we can provide a different implementation of these operands in order to use them. Since we can implement that custom implementation, it can happen that the result is different than we expect. This is something we can’t do with the is not
operator.
For example, in our class Vehicle
, we can add:
public static bool operator ==(Vehicle vehicle, Vehicle otherVehicle) { if (otherVehicle is not null) { return otherVehicle.VehicleBrand == vehicle.VehicleBrand; } return false; } public static bool operator !=(Vehicle vehicle, Vehicle otherVehicle) { if (otherVehicle is not null) { return otherVehicle.VehicleBrand != vehicle.VehicleBrand; } return false; }
The overload of ==
operator returns true only if the compared vehicles have the same brand, while the overload of !=
operator returns true only if they have a different brand. In all the other cases they return false
.
To check the behavior, we can create two objects:
var vehicle = new Vehicle() { SerialNumber = 1001, VehicleBrand = Brand.Toyota }; var otherVehicle = new Vehicle() { SerialNumber = 1002, VehicleBrand = Brand.Toyota };
Since they have the same Brand
property, we can compare these objects with !=
:
Console.WriteLine(vehicle != otherVehicle);
And we get:
False
The same happens even if they are effectively different.
Equally, if we compare to null:
Console.WriteLine(vehicle != null);
We will get false as output, since the overload of !=
checks the VehicleBrand
property, only if the right-hand element is not null.
In this case, we’ve modified the standard behavior of !=
operator, and this can not happen with is not
.
Conclusions
In this article, we’ve learned about the differences between != and is not operators. We can take into account these differences when implementing our code.