简体   繁体   中英

Result of calling IEquatable<T>.Equals(T obj) when this == null and obj == null?

What should IEquatable<T>.Equals(T obj) do when this == null and obj == null ?

1) This code is generated by F# compiler when implementing IEquatable<T> . You can see that it returns true when both objects are null :

public sealed override bool Equals(T obj)
    {
        if (this == null)
        {
            return obj == null;
        }
        if (obj == null)
        {
            return false;
        }

        // Code when both this and obj are not null.
    }

2) Similar code can be found in the question " in IEquatable implementation is reference check necessary " or in the question " Is there a complete IEquatable implementation reference? ". This code returns false when both objects are null .

public sealed override bool Equals(T obj)
    {
        if (obj == null)
        {
            return false;
        }

        // Code when obj is not null.
    }

3) The last option is to say that the behaviour of the method is not defined when this == null .

leppie is right. Just to elaborate on his answer (and confirm his suspicion that F# doesn't guarantee this != null) : discriminated unions may be marked with the attribute [<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>] allowing cases to be represented by the value null. Option<'T> is such a type. The None case is represented by null at run-time. (None : option<int>).Equals(None) is syntactically valid. Here's a fun example:

[<CompilationRepresentation(CompilationRepresentationFlags.UseNullAsTrueValue)>]
type Maybe<'T> =
  | Just of 'T
  | Nothing
  [<CompilationRepresentation(CompilationRepresentationFlags.Instance)>]
  member this.ThisIsNull() = match this with Nothing -> true | _ -> false

Decompiling ThisIsNull with Reflector shows

public bool ThisIsNull()
{
    return (this == null);
}

And the result:

Nothing.ThisIsNull() //true

The reason F# does this (I suspect) to optimize empty lists as null .

By adding this check, it allows one to call an instance method on a null instance without any problems.

See my blog post from a while back.

In C#, this is irrelevant.

To answer the question:

It should return true as both instances are null and deemed equal.

If this is null, the code can't be called, so that case needn't be considered (in C# anyway, there are cases where languages allow a null object to have a method dereferenced though obviously if it internally examines any of its non-existent fields it will error. Consider:

return x.Equals(y);

If x is null, we don't even get to call into Equals for the null check to count.

Hence we need only consider:

public bool Equals(T obj)
{
  if(obj == null)
    return false;
  //logic defining equality here.
}

Where the possibility of both objects being null does come up, is when we are examining them from a static == operator override or from an IEqualityComparer<T> implementation:

public bool Equals(T x, T y)
{
  if(x == null)
    return y == null;
  if(y == null)
    return false;
  //logic defining equality here.
}

Note that a useful shortcut here if equality can be lengthy to determine (eg comparing long strings), then we may take advantage of the fact that identity entails equality - that is something is always equal to itself, even Ayn Rand could figure that out ;) There are also algorithms that make comparing an item with itself quite common, making this shortcut well worth including. In this case the identity comparison already includes the check for both being null, so we leave it out again:

public bool Equals(T x, T y)
{
  if(ReferenceEquals(x, y))
    return true;
  if(x == null || y == null)
    return false;
  //logic defining equality here.
}

For most methods I assume undefined behavior when called with this==null . That's because most programmers write their code under the assumption that this!=null , which is guaranteed by the C# specification if the calling code is written in C#.

That's why every sane caller of x.Equals(y) should either know for sure that that x is not null , or add a manual null check.

In most cases I wouldn't call Equals directly at all, but instead use EqualityComparer<T>.Default .

I would definitelly go with option 1:

    if (this == null)
    {
        return obj == null;
    }
    if (obj == null)
    {
        return false;
    }

null object always equals null object.

如果this == null,您将在该对象上收到调用Equals()的运行时异常。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM