C# SortList复制了密钥,但不应';T
我有一个实现IList接口的类。我需要这个列表的“排序视图”,但不修改它(我不能直接对IList类进行排序) 当修改原始列表时,应更新这些视图,保持项目排序。因此,我引入了一个SortList创建方法,该方法创建一个SortList,该SortList具有原始列表中包含的特定对象的比较器 以下是代码片段:C# SortList复制了密钥,但不应';T,c#,sorting,duplicates,C#,Sorting,Duplicates,我有一个实现IList接口的类。我需要这个列表的“排序视图”,但不修改它(我不能直接对IList类进行排序) 当修改原始列表时,应更新这些视图,保持项目排序。因此,我引入了一个SortList创建方法,该方法创建一个SortList,该SortList具有原始列表中包含的特定对象的比较器 以下是代码片段: public class MyList<T> : ICollection, IList<T> { public SortedList CreateSortVi
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>();
}
在我看来,这是一个多线程问题。我看不出Lock()函数在您的代码中做了什么,但我认为如果使用标准锁围绕字典访问代码,您会更幸运:
lock(this){
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++;
}
删除部分也是如此。多亏了DigeMail的评论,我找到了一个快速的解决方案:IComparer实现只在真正相等的对象上返回0
因此:
public int比较(对象x,对象y)
{
IComparable propX=(IComparable)mSortingProperty.GetValue(x,null);
IComparable propY=(IComparable)mSortingProperty.GetValue(y,null);
整数比较;
如果((比较=propX.CompareTo(propY))==0){
if(x.GetHashCode()y.GetHashCode())
回报率(+1);
否则返回(0);
}否则
返回(比较);
}
如果我理解正确,您的目的只是获取列表的视图,按对象的属性排序
那么,当您可以使用LINQOrderBy
(或者如果您使用的是.net 2.0List.Sort()
)轻松获得结果时,为什么要使用需要唯一键的SortedList
因此,例如,您的CreateSortView
可以这样实现:(省略锁、try/finally和引用计数)
编辑: 如果您需要频繁地从分类收集中添加/删除项目,可能使用SortedList会更好,但SortedList的问题是它需要唯一的键,而在您的情况下,您无法保证这一点 无论如何,您可以使用不需要唯一值的自定义排序列表,请查看下面的链接以获得一个简单的实现:
我忘了说这只发生在某些对象值(大约5%)上。因此,虽然原始列表包含100项,但“排序视图”仅包含95项。当
propX.CompareTo(propY)
返回0时,是否可以通过设置断点来调试GenericPropertyComparer
?因此,您将能够了解比较器是否正常工作,以及值是否实际相等…@digEmAll您明白了!我注意到IComparable.CompareTo不调用GetHashCode!哎哟我需要一些关于SortList IComparer函数的说明。现在我明白了为什么每个人都试图在SortList中复制密钥:他们希望对密钥进行排序,这是可以复制的(在我的例子中,“ApplicationName”视图是复制的,但“ApplicationPath”视图不是复制的)。关于锁定的次要反馈-如果在获取锁之前或期间失败(远角的情况),您将尝试释放您没有的锁。这与提出的问题无关,只是需要注意。@Marc Gravell我从未考虑过案例监视器。Enter失败(只需抛出ArgumenNullException)。怎么可能呢?数据实际上是由单个线程访问的(至少在创建视图的进程上)。您的代码几乎是等效的,因为Lock()例程确实监视.Enter(SyncRoot),但允许我以更复杂的方式限定独占对象访问的范围。在任何情况下,mSortListView都有一个string类型的键,不知道哪个字典正在使用您提供的自定义gethashcode作为键的对象。无论如何,在gethashcode上调用base不是一个好主意,因为您必须对有意义的属性进行散列。我在比较器中使用gethashcode
似乎有点奇怪,而且考虑到HashCode
对于两个不同的对象可以相等,这可能是错误的。看看我的答案,另一个解决方案。我改变了设计:现在泛型参数T应该派生自IComparable,以避免GetHashCode()比较。@卢卡:是的,这样更好。无论如何,我仍然认为,使用SortedList
来获得一个按项目的单个属性排序的列表(要解决的关键唯一性约束比较复杂),对我来说似乎是一种过度工作。按需对原始列表进行排序(IMO:)更简单,这肯定取决于添加/删除元素的频率(这需要排序操作来更新“视图”)。是的,如果您经常添加/删除元素,您是对的。在这种情况下,分类列表更好。另一种解决方案是使用二进制搜索删除/添加已排序的列表,这样基本上可以获得与SortedList相同的性能。根据您的注释进行编辑;-)
lock(this){
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++;
}
public int Compare(object x, object y)
{
IComparable propX = (IComparable)mSortingProperty.GetValue(x, null);
IComparable propY = (IComparable)mSortingProperty.GetValue(y, null);
int compare;
if ((compare = propX.CompareTo(propY)) == 0) {
if (x.GetHashCode() < y.GetHashCode())
return (-1);
else if (x.GetHashCode() > y.GetHashCode())
return (+1);
else return (0);
} else
return (compare);
}
public IList<T> CreateSortView(string property)
{
IList<T> sortView;
if (mSortListViews.ContainsKey(property) == false)
{
// Create sorted view
sortView = this.OrderBy(x => x, new GenericPropertyComparer<T>(property)).ToList();
mSortListViews.Add(property, sortView);
}
else
{
sortView = mSortListViews[property];
}
return sortView;
}
class GenericPropertyComparer<T> : IComparer<T>
{
public GenericPropertyComparer(string propertyName)
{
var property = typeof(T).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
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(T x, T y)
{
IComparable propX = (IComparable)mSortingProperty.GetValue(x, null);
IComparable propY = (IComparable)mSortingProperty.GetValue(y, null);
return (propX.CompareTo(propY));
}
private PropertyInfo mSortingProperty = null;
}