Modeling Equality in C#

Building models that represent business or processes is at the heart of most software. Models are abstractions of reality where we try to focus on what is fundamental to the problem at hand. Underlying every software model there is a theory about reality. A healthy theory will constantly adjust and adapt to more closely resemble reality as new insights are found that contradict the theory. Sometimes we find that a model is based on too many incorrect assumptions to be adjusted and needs to be revaluated. More often only slight course corrections are needed.

Given a system that models a business or process, equality will be a main tenant of the model. At it's face, equality seems simple but it can quickly become complex depending on what is being modeled. When we more closely examine equality in the context of a software model, we are looking at comparing entities in one of two contexts: equivalence or identity.


In a model, there will usually be entities that exist where time is irrelevant to the essence of the entity within the model. These entities can be said to be "equal" if they are equivalent. This means we can swap out one entity for another with the same value and it does not matter. These types of entities are called value objects in domain driven design.

An example of this type of entity is "money". If you have five dollars in your pocket and want to buy a sandwich, you generally don't care about the serial number on the bill. In this case five dollars is just five dollars.


In a software model, there are often entities that exist where the identity matters. These entities usually fall into one of two categories: 1) entities that need to be uniquely identified by the nature of what is being modeled or 2) entities that need to be tracked across time. Entities that need to be uniquely identified may or may not have a temporal dimension. Entities that need to be tracked across time may receive an arbitrary identifier in order to keep track of them. These types of objects are called entity objects in domain driven design.

An example of an entity that needs to be tracked but does not necessarily have a temporal dimension is also "money". But this time, instead of the domain being "purchasing-a-sandwich", it is a money laundering tracking system. In this new domain, we actually care about the serial number, or identifier on the bill itself more than the amount of the currency. If we can't track individual bills, we can not trace them back to their source.

An example of an entity that needs to be tracked over time for the sake of temporality is a person in a hospital patient tracking system. In this case, we need an identifier to track a person over time and have a history of their symptoms and medications. This identifier could be naturally in the model like a government issued identifier (for example social security number), or it could be an arbitrary number created by a database. The point is that we need to know about this entity and track it's events over time. Even though many software models skip tackling the concept of time and entity temporality head-on it is still implicit in the entities within the model and how they are uniquely identified.

Equivalence, Identity and "Null"

Checking equality between two object instances is either a check for equivalence or a check for matching identity depending on the domain being modeled. Equivalence checks are performed on value objects and identity checks are performed on entity objects. Null checking often does not fit well into a business domain model. It is better to make a clear distinction in a code base between null checks and equality checks. In C#, we should use the "ReferenceEquals()" method to check references and save "Equals" and the equality operator "==" for testing equality.