Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the reasoning to fallback to Object's == operator when one operand is an interface?

Tags:

c#

Consider the following types:

class A { }
class B { }
interface IC { }

A a = null; // the value doesn't matter - null or anything else, for all three
B b = null;
IC c = null;

The following does not compile:

var x = a == b;

But the following does compile (as I was surprised to discover):

var x = a == c;

As I understand the compiler falls back to using the default == operator, which is defined on object and thus accepts any type for its arguments. The IL looks like this (ignore the details of ldfld):

ldarg.0
ldfld class A a
ldarg.0
ldfld class IC c
ceq
stloc.0

In other words it uses reference equality.

My questions:

  1. In terms of language design, why does this make sense? To me it doesn't, and I consider it a big pitfall.

  2. If this is indeed a pitfall, shouldn't Code Analysis warn us about it? (nope - it doesn't). By the way, ReSharper seems to have this feature.

like image 786
sinelaw Avatar asked Feb 04 '13 22:02

sinelaw


2 Answers

The reason that the second line compiles is that there may be another class that derives from A and implements IC.

public class D : A, IC {}
...
a = new D(); c = a; var t = a == c; //t = true;

Classes can only inherit from one class, so you can never create a class that inherits from both A and B unless A is a descendent of B or vice-versa.

like image 88
Nick Bray Avatar answered Nov 06 '22 03:11

Nick Bray


The reason that the a == b test does not compile is because the compiler has enough information to know that the test cannot possibly be ever true as the two classes are not in the same hierarchy. Therefore it's the equivalent of the compiler not allowing you to write a condition that is in reality a constant by mistake.

For the interface comparison the compiler sees that there is an operator==(object, object) that it can use, and since both A and IC are implicitly convertible to object that's what in fact must happen. There can very well be another type (not even necessarily a reference type) that implements IC so the condition is genuine.

The R# warning is one I appreciate; as the page says, equality comparison between interface types is in some cases somewhat dubious and there an extremely descriptive, simple to replace with solution: object.ReferenceEquals. Of course there is the counter-argument that the equality operator may have been overloaded so the test might be meaningful. Or it can simply be someone writing in a more terse style. Obviously though banning such use in the compiler is somewhat heavy-handed.

like image 37
Jon Avatar answered Nov 06 '22 03:11

Jon