Skip to content

Misleading notes about equality testing methods #29582

@gaazkam

Description

@gaazkam

Section [§ Object Identity vs. Value Equality[(https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/object-oriented/objects#object-identity-vs-value-equality) has this to say:

To determine whether two class instances refer to the same location in memory (which means that they have the same identity), use the static Object.Equals method. (System.Object is the implicit base class for all value types and reference types, including user-defined structs and classes.)

This is untrue in the general case. As per the docs of the Equals(Object, Object) method, "If the two objects do not represent the same object reference and neither is null, it calls objA.Equals(objB) and returns the result. This means that if objA overrides the Object.Equals(Object) method, this override is called."

Section [Objects - create instances of types § Object Identity vs. Value Equality[(https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/object-oriented/objects#object-identity-vs-value-equality) later says:

To determine whether the values of the fields in two class instances are equal, you might be able to use the Equals method or the == operator. However, only use them if the class has overridden or overloaded them to provide a custom definition of what "equality" means for objects of that type. The class might also implement the IEquatable interface or the IEqualityComparer interface. Both interfaces provide methods that can be used to test value equality. When designing your own classes that override Equals, make sure to follow the guidelines stated in How to define value equality for a type and Object.Equals(Object).

The reader may understand that public static bool Equals (object? objA, object? objB); always returns true if and only if both objects have the same reference, while public virtual bool Equals (object? obj); will behave according to what the programmer has defined. This is misleading.

To fix this issue I suggest that the first paragraph is reworded to the following:

To determine whether two class instances refer to the same location in memory (which means that they have the same identity), use the static Object.ReferenceEquals method. (System.Object is the implicit base class for all value types and reference types, including user-defined structs and classes.)

Object.ReferenceEquals, and not Object.Equals has the semantics as described here.

A similar issue is present in the same section when it talks about value types:

To determine whether the instance fields in two struct instances have the same values, use the ValueType.Equals method. Because all structs implicitly inherit from System.ValueType, you call the method directly on your object as shown in the following example:

(...)

The System.ValueType implementation of Equals uses boxing and reflection in some cases. For information about how to provide an efficient equality algorithm that is specific to your type, see How to define value equality for a type. Records are reference types that use value semantics for equality.

This seems to be self-refuting: the first paragraph says that ValueType.Equals checks all fields to test for equality while the second says that the programmer can override ValueType.Equals, so it might not do this.

I am not sure if there is any built in method that guarantees to always compare fields. If there is, then this paragraph may be fixed in a similar way to the previous paragraph: this method may be mentioned here instead of ValueType.Equals.

If there is not, then I think this paragraph may be reworded in the following way:

The ValueType.Equals method, by default, determines whether the instance fields in two struct instances have the same values. Because all structs implicitly inherit from System.ValueType, you call the method directly on your object as shown in the following example:

(...)

The default System.ValueType implementation of Equals uses boxing and reflection in some cases. For information about how to provide an efficient equality algorithm that is specific to your type, see How to define value equality for a type. Records are reference types that use value semantics for equality.

The difference here is that I tried to avoid wording that seems to make promises it cannot keep. If it says "To determine whether the instance fields in two struct instances have the same values, use the ValueType.Equals method" then the reader may understand they can depend on this behavior of this method, that this behavior is guaranteed. But this is not the case.


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.


Associated WorkItem - 476421

Metadata

Metadata

Labels

📌 seQUESTeredIdentifies that an issue has been imported into Quest.dotnet-csharp/svcfundamentals/subsvcin-prThis issue will be closed (fixed) by an active pull request.okr-qualityContent-quality KR: Concerns article defects (bugs), freshness, or build warnings.

Type

No type

Projects

Status

✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions