我有一个实现IList接口的类。我需要这个列表的“排序视图”,但不修改它(我不能直接对IList类进行排序)。
修改原始列表时,应更新这些视图,保持项目的排序。所以,我引入了一个SortList创建方法,该方法创建一个SortList,它具有原始列表中包含的特定对象的比较器。
以下是代码片段:
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()
);
}
答案 0 :(得分:1)
在我看来,这是一个多线程问题。我无法看到你的代码中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++;
}
和删除部分相同。
答案 1 :(得分:1)
如果我理解正确,你的目的只是为了查看你的列表,按对象的属性排序。
然后,当您使用LINQ SortedList
(或者如果您使用.net 2.0 OrderBy
)轻松获得结果时,为什么要使用需要唯一键的List.Sort()
?
因此,例如,您的CreateSortView
可以通过这种方式实施:
(省略锁定,尝试/最后和引用计数)
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;
}
实施GenericPropertyComparer
如下:
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;
}
修改强>
如果你需要频繁地添加/删除已排序集合中的项目,可能使用SortedList会更好,但SortedList的问题是它需要唯一的密钥,在你的情况下你无法保证。
无论如何,您可以使用不需要唯一值的自定义排序列表,请查看下面的链接以获得简单的实现:
Implementation of sorted IList<T>
that doesn't require unique values
答案 2 :(得分:0)
感谢 digEmAll 的评论,我找到了一个快速的解决方案:IComparer实现只返回真正等于对象的0!
所以:
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);
}