A state aware generic list
For so many years I’ve written code that needed to know whether or not a collection of objects has been changed or not. Now I’ve finally written the code that tells me just that.
The scenario is typical. You have a class called Order with a property called OrderLines which is a generic List containing OrderLine objects. You retrieve the Order class from the database including all the OrderLines, change some properties and then you save the Order object back to the database. Now the question is if you need to save the OrderLines as well. To know that, you need to know if the OrderLines collection has been changed. It would be a waste of resource to persist all the order lines each time you make a modification to the Orders object. To know if it has changed, we need a list that is aware of its own state.
The StateList<T>
This class is a subclass of the List<T> generic class but with a few twists. It does exactly what a normal List<T> does, but has two new members – a MarkUnChanged method and an IsChanged property. It also overrides the GetHashCode method. That’s all it does.
The MarkUnChanged method tells the StateList what the original list contains. You call this method after you’ve retrieved all the OrderLines and populated the list. That let’s the StateList know what the comparison is for reference. Behind the scenes, the MarkUnChanged method stores the hash code of the items in the list in a private integer variable.
The IsChanged property returns a Boolean value that indicates whether or not the items in the list produce the same hash code as the one stored in the private variable. If it doesn’t, then the list is changed. This is the class.
[System.Serializable]
public class StateList<T> : System.Collections.Generic.List<T>
{
public override int GetHashCode()
{
long hash = 0;
foreach (T item in this)
{
hash += item.GetHashCode();
}
return hash.GetHashCode();
}
private int _HashCode = 0;
/// <summary>
/// Gets if this list's data has been changed.
/// </summary>
public virtual bool IsChanged
{
get
{
return this.GetHashCode() != _HashCode;
}
}
/// <summary>
/// Marks the object as being clean,
/// which means not changed.
/// </summary>
public virtual void MarkUnChanged()
{
_HashCode = this.GetHashCode();
base.TrimExcess();
}
}
Example
Here is a little taste of how it works:
StateList<int> list = new StateList<int>();
list.Add(3);
list.Add(4);
list.Add(5);
// list.IsChanged = true
list.MarkUnChanged();
// list.IsChanged = false
list.Add(6);
// list.IsChanged = true
list.Clear();
list.Add(3);
list.Add(4);
list.Add(5);
// list.IsChanged = false
The order of the items
The code above produces the same hash code no matter in what order the individual items are located in the list. So if the same items are located in the list but at different locations, the IsChanged property is false. To support location awareness, you can just switch the overridden GetHashCode method with this one:
public override int GetHashCode()
{
long hash = 0;
for (int i = 0; i < this.Count; i++)
{
hash += this[i].GetHashCode() ^ i;
}
return hash.GetHashCode();
}