Lesson Weekend

Before we add functionality to our to do list that will save new Items to our database, we need to consider a specific problem that comes up when comparing two objects.

We'll start with a new test that demonstrates the problem.

ToDoList.Tests/ModelTests/ItemTests.cs
[TestMethod]
public void Equals_ReturnsTrueIfDescriptionsAreTheSame_Item()
{
  // Arrange, Act
  Item firstItem = new Item("Mow the lawn");
  Item secondItem = new Item("Mow the lawn");

  // Assert
  Assert.AreEqual(firstItem, secondItem);
}

In the eyes of a user, firstItem and secondItem are exactly the same. Both are Items for mowing the lawn. However, if we run our tests, we'll get the following error:

Error Message:
 Assert.AreEqual failed. Expected:<ToDoList.Models.Item>. Actual:<ToDoList.Models.Item>.

From the perspective of our application, firstItem and secondItem are two different objects that happen to have the same Description property.

From the perspective of our tests, we want these two objects to be seen as the same. Our test should be able to instantiate an Item and then expect that an Item returned from our database is equal to the first Item if they have the same properties. However, when a record is retrieved from the database, C# has to convert it into a new object. That means it will always be a different object from the first Item.

We need to update our tests so our application knows when we want two different objects to be considered the same.

Overriding Built-In Methods


We can fix this issue by including a special method (note that best practice dictate that this method be below the properties and constructors but above the other methods in our file):

ToDoList/Models/Item.cs
...

    public override bool Equals(System.Object otherItem)
    {
      if (!(otherItem is Item))
      {
        return false;
      }
      else
      {
        Item newItem = (Item) otherItem;
        bool descriptionEquality = (this.Description == newItem.Description);
        return descriptionEquality;
      }
    }

...

Let's go over this method line by line.

  • The method Equals() is built into C#. It's included in a set of default behaviors all objects have. If we want to override it, Microsoft recommends we do so with the override keyword, which we use here.

  • Because Equals() accepts any type of object, we must declare its argument as the generic System.Object type. That's why our parameter specifies that otherItem is a System.Object, not an Item.

  • Next, we have a conditional that checks if the argument passed into the parameter otherItem is in fact an Item object. If it isn't, our method will return false. At the very least, we know that we want both objects to be of the same type.

  • Next, we use typecasting to ensure that otherItem is in fact an Item. This may seem like a redundant piece of code but our method will break without it.

  • We can now compare the Description of this (the Item our method will be called on) to the Description of the newItem. If they are the same, our application should consider both Items to be exactly the same. If we omit the typecasting in the previous line, we'll get the following error when we try to run our tests: 'object' does not contain a definition for 'Description' and no accessible extension method 'Description' accepting a first argument of type 'object' could be found.

Our new test will now pass. However, there is now a new warning: 'Item' overrides Object.Equals(object o) but does not override Object.GetHashCode(). This warning is letting us know that we also need to override the GetHashCode() method if we want the Equals method to also evaluate dictionary keys as the same. For simplicity's sake, we will not worry about this warning, but you are welcome to explore the documentation further.

Now that we have our new method in place, we're ready to write and test a method for saving Items to the database.

If you'd like to read more about best practices concerning overriding Equals(), check out this documentation.

Repository Reference

Follow the link below to view how a sample version of the project should look at this point. Note that this is a link to a specific commit in the repository.

Example GitHub Repo for To Do List

Lesson 12 of 36
Last updated more than 3 months ago.