【发布时间】:2011-06-06 01:03:42
【问题描述】:
我有一个实现 IList 接口的类。我需要此列表的“排序视图”,但无需修改它(我无法直接对 IList 类进行排序)。
当原始列表被修改时,这些视图将被更新,保持项目排序。因此,我介绍了一种 SortList 创建方法,该方法创建一个 SortList,其中包含原始列表中包含的特定对象的比较器。
这里是sn-p的代码:
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
{
public SortListView(string property, int capacity)
: base(new GenericPropertyComparer(typeof(T).GetProperty(property, BindingFlags.Instance | BindingFlags.Public)), capacity)
{
}
public int ReferenceCount = 0;
public void Add(T item)
{
Add(item, item);
}
public void Remove(T item)
{
base.Remove(item);
}
class GenericPropertyComparer : IComparer
{
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;
}
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));
}
private PropertyInfo mSortingProperty = null;
}
private Dictionary<string, SortListView> mSortListViews = new Dictionary<string, SortListView>();
}
实际上,类用户请求创建一个 SortListView 指定确定排序的属性名称,并使用反射每个 SortListView 定义一个 IComparer 来保持对项目的排序。 每当从原始列表中添加或删除项目时,每个创建的 SortListView 都将使用相同的操作进行更新。
这似乎很好,但它给我带来了问题,因为它在将项目添加到 SortList 时给了我以下异常:
System.ArgumentException:已添加项目。在字典中键入:'PowerShell_ISE [C:\Windows\sysWOW64\WindowsPowerShell\v1.0\PowerShell_ISE.exe]' 正在添加的键:'PowerShell_ISE [C:\Windows\system32\WindowsPowerShell\v1.0\PowerShell_ISE.exe]'
从SortedListView.Add(object)抛出的异常信息中可以看出,键(列表项对象)的字符串表示不同(注意可执行文件的路径)。
为什么 SortList 给我这个例外?
为了解决这个问题,我尝试为底层对象实现GetHashCode(),但没有成功:
public override int GetHashCode()
{
return (
base.GetHashCode() ^
mApplicationName.GetHashCode() ^
mApplicationPath.GetHashCode() ^
mCommandLine.GetHashCode() ^
mWorkingDirectory.GetHashCode()
);
}
【问题讨论】:
-
我忘了说这只发生在某些对象值上(大约 5%)。因此,虽然原始列表包含 100 个项目,但“排序视图”仅包含 95 个项目。
-
当
propX.CompareTo(propY)返回 0 时,您不能尝试通过设置断点来调试您的GenericPropertyComparer吗?因此,您将能够了解比较器是否工作正确以及值是否实际上等于... -
@digEmAll 你明白了!我注意到 IComparable.CompareTo 不调用 GetHashCode!哎哟!我需要对 SortList IComparer 函数进行一些说明。现在我明白了为什么每个人都试图在 SortList 中复制键:他们想要对可以复制的键进行排序(在我的例子中,视图“ApplicationName”是重复的,但不是“ApplicationPath”)。
-
对您的锁定的次要反馈 - 如果在锁定获取之前或期间失败(极端情况),您尝试释放您没有的锁定。这与提出的问题无关 - 只是需要注意。
-
@Marc Gravell 我从未考虑过 Monitor.Enter 失败的情况(只是抛出 ArgumenNullException)。怎么可能?
标签: c# sorting duplicates