CodeQL documentation

Equals should not apply “is”

ID: cs/equals-uses-is
Kind: problem
Severity: warning
Precision: medium
Tags:
   - reliability
   - maintainability
Query suites:
   - csharp-security-and-quality.qls

Click to see the query in the CodeQL repository

The method signature of Equals takes an object as an argument. It is therefore common to test whether the parameter is of the same type as the object that Equals is being called on. This should not be done using is because this technique does not limit the argument to being the exact same type as the object. It will not return false if the argument is a subtype of the object’s type.

As an exception to the rule, it is acceptable to use is when your class is sealed, because the concern about subclassing does not then apply. That said, it is probably best to avoid it even then, because sealed classes can sometimes become unsealed later - it’s sensible to try and avoid potential problems down the line.

Recommendation

Call GetType() on the argument and compare it with the type of the current object instead.

Example

The following example clearly demonstrates the problem with using is. The example outputs:

b does equal d.
d does not equal b.

This asymmetry violates the contract of the Equals method.

class EqualsUsesIs
{
    class BaseClass
    {
        public override bool Equals(object obj)
        {
            return obj is BaseClass;
        }
    }

    class DClass : BaseClass
    {
        public override bool Equals(object obj)
        {
            return obj is DClass;
        }
    }

    public static void Main(string[] args)
    {
        BaseClass b = new BaseClass();
        DClass d = new DClass();
        Console.WriteLine("b " + (b.Equals(d) ? "does" : "does not") + " equal d.");
        Console.WriteLine("d " + (d.Equals(b) ? "does" : "does not") + " equal b.");
    }
}