Simple types (strings and primitives) are typically tested for equality with the ==
and !=
.
Reference types (Instances of classes) are compared using the Equals()
method inherited from Object
. If your goal with the equality test is to ensure that two objects are the exact same instance then relying on Object
's implementation will suffice. If not, you need to override object.Equals()
.
If you know that all the instances of your class are created in one place, say characters in some game or simulation then reference equality is sufficient. However, it is likely that multiple instances of the same real-world entity will be created (from a database, by user input, via a web request). In this case values that uniquely identify the entity must be tested for equality. Therefore Equals()
must be overridden and appropriate data members of your class are tested for equality.
An overridden Equals()
method will contain equality tests on members of simple types using ==
and reference types with recursive calls to Equals()
.
The Object
class provides appropriate methods to compare two objects to detect if they are one and the same instance.
Object.GetHashCode()
The Object.GetHashCode()
method returns a hash code in the form of a 32 bit integer. The hash code is used by dictionary and set classes such as Dictionary<T>
and HashSet<T>
to store and retrieve objects in a performant manner.
The relationship between hash code and equality is that if two objects are equal (Equal()
returns true) then GetHashCode()
for the two objects must return the same value. This does not apply in the reverse direction. It is not symmetrical. Just because two objects have the same hashcode they do not have to be equal. Picture a lookup function that first goes to a "bucket" based on the hash code and then picks out the particular item using the equality test.
The HashCode library API documentation discusses the best way to generate a hash code
The values used in the equality test must be stable while the hashed collection is in use. If you add an object to the collection with one set of values and then change those values the hash code will no longer point to the correct "bucket". In practice this means that the object should be immutable. Other approaches run the risk of creating gotchas for maintainers.
A set is a collection of unique values. The default .NET implementation of a set is the HashSet<T>
class, which is a collection that only contains unique values.
You are working on a system to simplify the login process for your organization's network. The tasks concern the authentication part. The system uses facial recognition to prove identity.
In all occurrences the eye color parameter is guaranteed to be non-null.
Implement Authenticator.AreSameFace()
to check that two faces match.
Add equality routines for the FacialFeatures
class.
Authenticator.AreSameFace(new FacialFeatures("green", 0.9m), new FacialFeatures("green", 0.9m);
// => true
Authenticator.AreSameFace(new FacialFeatures("blue", 0.9m), new FacialFeatures("green", 0.9m);
// => false
Despite your protests the system administrator insists on having a built-in identity during acceptance testing so that they can always be authenticated.
The admin's email is admin@exerc.ism. They have green eyes and a philtrum with a width of 0.9.
Add equality routines for the Identity
class.
Implement the Authenticator.IsAdmin()
method to check that the identity passed in matches that of the administrator.
var authenticator = new Authenticator();
authenticator.IsAdmin(new Identity("admin@exerc.ism", new FacialFeatures("green", 0.9m)));
// => true
authenticator.IsAdmin(new Identity("admin@thecompetition.com", new FacialFeatures("green", 0.9m)));
// => false
Implement the Authenticator.Register()
method which stores an identity on the authenticator itself such that calls to IsRegistered()
will return true
for this identity: otherwise IsRegistered()
returns false
.
To detect duplicated attempts to register an identity, if the identity has already been registered then false
is returned by Authenticator.Register()
, otherwise true
.
var authenticator = new Authenticator();
authenticator.Register(new Identity("tunde@thecompetition.com", new FacialFeatures("blue", 0.9m)));
// => true
authenticator.IsRegistered(new Identity("tunde@thecompetition.com", new FacialFeatures("blue", 0.9m)));
// => true
authenticator.Register(new Identity("tunde@thecompetition.com", new FacialFeatures("blue", 0.9m)));
// => false
Implement the Authenticator.IsRegistered()
method and ensure it returns false when no identities have been registered.
var authenticator = new Authenticator();
authenticator.IsRegistered(new Identity("alice@thecompetition.com", new FacialFeatures("blue", 0.8m)));
// => false
A bug has been reported whereby the Authenticator.IsRegistered()
method is called multiple times in quick succession for the same identity. You believe that there is some sort of "bounce" problem where the exact same record is being submitted multiple times. Your task is to add a diagnostic routine Authenticator.AreSameObject()
to support any testing that's undertaken. The routine compares to objects and returns true
if they are the exact same instance otherwise false
.
var identityA = new Identity("alice@thecompetition.com", new FacialFeatures("blue", 0.9m));
var identityB = identityA;
Authenticator.AreSameObject(identityA, identityB);
// => true
var identityC = new Identity("alice@thecompetition.com", new FacialFeatures("blue", 0.9m));
var identityD = new Identity("alice@thecompetition.com", new FacialFeatures("blue", 0.9m));
Authenticator.AreSameObject(identityC, identityD);
// => false
Sign up to Exercism to learn and master C# with 62 concepts, 175 exercises, and real human mentoring, all for free.