I have a class which implements IList interface. I requires a "sorted view" of this list, but without modifying it (I cannot sort directly the IList class).
These view shall be updated when the original list is modified, keeping items sorted. So, I've introduced a SortList creation method which create a SortList which has a comparer for the specific object contained in the original list.
Here is the snippet of code:
public class MyList<T> : ICollection, IList<T>
{
...
public SortedList CreateSortView(string property)
{
try {
Lock();
SortListView sortView;
if (mSortListViews.ContainsKey(property) == false) {
// Create sorted view
sortView = new SortListView(property, Count);
mSortListViews.Add(property, sortView);
foreach (T item in Items)
sortView.Add(item);
} else
sortView = mSortListViews[property];
sortView.ReferenceCount++;
return (sortView);
} finally {
Unlock();
}
}
public void DeleteSortView(string property)
{
try {
Lock();
// Unreference sorted view
mSortListViews[property].ReferenceCount--;
// Remove sorted view
if (mSortListViews[property].ReferenceCount == 0)
mSortListViews.Remove(property);
} finally {
Unlock();
}
}
protected class SortListView : SortedList
{
/// <summary>
///
/// </summary>
/// <param name="property"></param>
/// <param name="capacity"></param>
public SortListView(string property, int capacity)
: base(new GenericPropertyComparer(typeof(T).GetProperty(property, BindingFlags.Instance | BindingFlags.Public)), capacity)
{
}
/// <summary>
/// Reference count.
/// </summary>
public int ReferenceCount = 0;
/// <summary>
///
/// </summary>
/// <param name="item"></param>
public void Add(T item)
{
Add(item, item);
}
/// <summary>
///
/// </summary>
/// <param name="item"></param>
public void Remove(T item)
{
// Base implementation
base.Remove(item);
}
/// <summary>
/// Compare object on a generic property.
/// </summary>
class GenericPropertyComparer : IComparer
{
#region Constructors
/// <summary>
/// Construct a GenericPropertyComparer specifying the property to compare.
/// </summary>
/// <param name="property">
/// A <see cref="PropertyInfo"/> which specify the property to be compared.
/// </param>
/// <remarks>
/// The <paramref name="property"/> parameter imply that the compared objects have the specified property. The property
/// must be readable, and its type must implement the IComparable interface.
/// </remarks>
public GenericPropertyComparer(PropertyInfo property)
{
if (property == null)
throw new ArgumentException("property doesn't specify a valid property");
if (property.CanRead == false)
throw new ArgumentException("property specify a write-only property");
if (property.PropertyType.GetInterface("IComparable") == null)
throw new ArgumentException("property type doesn't IComparable");
mSortingProperty = property;
}
#endregion
#region IComparer Implementation
public int Compare(object x, object y)
{
IComparable propX = (IComparable)mSortingProperty.GetValue(x, null);
IComparable propY = (IComparable)mSortingProperty.GetValue(y, null);
return (propX.CompareTo(propY));
}
/// <summary>
/// Sorting property.
/// </summary>
private PropertyInfo mSortingProperty = null;
#endregion
}
}
/// <summary>
/// Sorted views of this ReactList.
/// </summary>
private Dictionary<string, SortListView> mSortListViews = new Dictionary<string, SortListView>();
}
Practically, class users request to create a SortListView specifying the name of property which determine the sorting, and using the reflection each SortListView defined a IComparer which keep sorted the items.
Whenever an item is added or removed from the original list, every created SortListView will be updated with the same operation.
This seems good at first chance, but it creates me problems since it give me the following exception when adding items to the SortList:
System.ArgumentException: Item has already been added. Key in dictionary: 'PowerShell_ISE [C:\Windows\sysWOW64\WindowsPowerShell\v1.0\PowerShell_ISE.exe]' Key being added: 'PowerShell_ISE [C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell_ISE.exe]'
As you can see from the exception message, thrown by SortedListView.Add(object), the string representation of the key (the list item object) is different (note the path of the executable).
Why SortList give me that exception?
To solve this I tried to implement a GetHashCode implementation for the underlying object, but without success:
public override int GetHashCode()
{
return (
base.GetHashCode() ^
mApplicationName.GetHashCode() ^
mApplicationPath.GetHashCode() ^
mCommandLine.GetHashCode() ^
mWorkingDirectory.GetHashCode()
);
}