In this article, we are going to explore the differences between != and Is Not operators in C#. 

To download the source code for this article, you can visit our GitHub repository.

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 notis 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 != forces us to have the same built-in type on both sides of the comparison. 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.

For the sake of simplicity, we didn’t override Equals and GetHashCode, but for the full operator overload implementation, you can read Operator Overloading in C# article.

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.