8

I have two objects(of the same type) which contains a prop myprop of type byte?. The properties are set to null. When I perform objectA.myprop.Equals(objectB.myprop) I get 'true' as a result although the MSDN code sample states that "Equals applied to any null object returns false."

I'm guessing C# uses a seperate overload for nullable type comparisons. I would like to know how C# internally treats objects versus nullable types in this case.

arviman
  • 5,087
  • 41
  • 48

4 Answers4

10

When you call it like that, it will use Nullable<T>.Equals(object) which shows the expected behaviour in the documentation:

(Return value is true if...)

The HasValue property is false, and the other parameter is null. That is, two null values are equal by definition.

Likewise for equality via ==, section 7.3.7 of the C# 4 spec (lifted operators) states:

For the equality operators == [and] != a lifted form of an operator exists if the operand types are both non-nullable value types and if the result type is bool. The lifted form is constructed by adding a single ? modifier to each operand type. The lifted operator considers two null values equal, and a null value unequal to any non-null value. If both operands are non-null, the lifted operator unwraps the operands and applies the underlying operator to produce the bool result.

(Emphasis mine.)

This is a general rule, in terms of the implementation of object.Equals:

The following statements must be true for all implementations of the Equals method. In the list, x, y, and z represent object references that are not null.

[...]

  • x.Equals(null) returns false.

So while it is a general rule, it doesn't apply in this specific case - because the value here isn't an object reference - it's a value type value. It's still somewhat surprising, unless you basically accept that Nullable<T> is a bit of a special case - it has specific C# compiler support and it has CLR support in terms of boxing and unboxing.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
4

I have edited this for clarity.

You misunderstood that quote. To give the full context:

string s = null;
string t = String.Empty; // Logically the same as ""

// Equals applied to any null object returns false.
bool b = (t.Equals(s));

This is saying that a reference to a string object does not equate to a null reference. You took that quote out of context, and interpreted it as a general rule, which it is not. However this is referring to references NOT nullable types.

When you are dealing with nullable primitive types:

The actual rule is

An equality comparison of two nullable types that are both null evaluates to true.

http://msdn.microsoft.com/en-us/library/2cf62fcy.aspx

Joe
  • 46,419
  • 33
  • 155
  • 245
  • In the case of the question, wouldn't the code throw a null exception if `objectA.myprop` was null rather than true even if the other property was also null? – Chris Nov 23 '11 at 21:46
  • Yes that would be a null reference exception. – Joe Nov 23 '11 at 21:48
  • @Chris - That would be correct if it was a null object. However, it wouldn't in the case of a nullable type as you can tell by the link provided by Joe and Jon. Or are you referring to `objectA` itself being null? – arviman Nov 23 '11 at 21:58
  • @arviman My understanding is if `objectA` was null then yes a null reference exception will be thrown. but if `objectA.myProp` was null also, surely the `Equals` method is being executed as an extension method on `myProp` which is null? also resulting in an Exception? – Chris Nov 23 '11 at 22:01
  • @Chris - A reference to a nullable type exists even if it's null. For Nullable I personally like to think of `null` being another possible value along with the allowed values of T. I hope that makes more sense. – arviman Nov 23 '11 at 22:05
  • @arviman it does, as i was writing my previous comment I could see what was coming and doubt seeped in, but thought to ask anyway to be sure. Reading below on Jon Skeets answer also shed some light by it using the `HasValue` property. Thanks for clearing it up for me though! =) – Chris Nov 23 '11 at 22:09
  • I disagree with the bit which claims it's not a general rule - it *is*, but for all reference types. So it's not specific to string, but it's also not applicable to `Nullable`. See my answer for more details. – Jon Skeet Nov 23 '11 at 22:56
  • I bow to your superior judgement. I had a feeling this would happen when I saw you had answered the same question that I had! – Joe Nov 23 '11 at 23:01
  • What I missed on the first reading is that the example arviman gave concerned references to strings, but he was asking about nullable types. I think I went along with the conflation. – Joe Nov 23 '11 at 23:04
2

Nullable type is a struct and a struct never get a null value, so when a nullable type equals with null, this is means that this variable not really null rather a implicit cast occurred and that variable cast to a value type(nullable) with property Value=null. So:

int? a=null;//(a) get a memory space with value property = null
a.GetHashcode();//if (a) really is null must throw a exception but not throw 
Reza ArabQaeni
  • 4,848
  • 27
  • 46
0

I would claim that Nullable comparison X==Y behave exactly as should intuitively be expected, but I sense a possible misunderstanding here regarding X.Equals(Y), which behaves as if it is a static method call.

object A, B;
MyStruct? X, Y;

The quote "Equals applied to any null object returns false." is true for the non-static method A.Equals(B), and should typically hold for any sensible overrides of it.

However, the only reason this is so, is because A.Equals is a non-static method belonging to A, meaning A can never be null. Attempting A.Equals(B) with A = null would indeed throw a NullReferenceException.

The reason this has to be so is that the instance referred to by A might be of a more specific type than the declared variable A, and this more specific type might in turn override the A.Equals method. In other words, if A is null, then the runtime have no idea which implementation of A.Equals to use, and must therefore throw an Exception.

However static methods can't be overridden, meaning A can be null because the method to run is known already at compile-time. (This is why calling static methods is marginally faster than calling their non-static counterparts, and also why the compiler may decide to inline some static calls, but I am digressing.)

So, for any static implementations both operands can be null. Also, null should always be considered the same as null, and different from any other value. (If an implementation of Equals deviates from this common assumption, then that will typically be considered a bug.)

Examples of static Equals methods:

A==B;
A!=B;
MyClass.Equals(A, B);
object.Equals(A, B);
ReferenceEquals(A, B);

In this regard Nullable behave the same way as objects. The only difference being that X.Equals(Y) supports either X or Y or both to be null without throwing an Exception. So it is more correct to say that it behaves like the static equality methods mentioned above, which is typically more desirable.

But how is it possible for X.Equals(Y) to behave like a static method? This is because Nullable is a struct. Structs in C# does not support inheritance, so there is no difference between the type of the variable and the type of the instance. Which is why the runtime is never in doubt of which method Equals refers to. And so no Exception is needed. Any value supported by the struct can therefore be supported by the method for both operands, and null is therefore just another value.

Now, in addition to this there is some built-in special support for syntactical sugar and optimizations for Nullable types, but the behavior remains, as far as I know, as I described above.

Jan Heldal
  • 148
  • 6