C# SortList复制了密钥,但不应';T

C# SortList复制了密钥,但不应';T,c#,sorting,duplicates,C#,Sorting,Duplicates,我有一个实现IList接口的类。我需要这个列表的“排序视图”,但不修改它(我不能直接对IList类进行排序) 当修改原始列表时,应更新这些视图,保持项目排序。因此,我引入了一个SortList创建方法,该方法创建一个SortList,该SortList具有原始列表中包含的特定对象的比较器 以下是代码片段: public class MyList<T> : ICollection, IList<T> { public SortedList CreateSortVi

我有一个实现IList接口的类。我需要这个列表的“排序视图”,但不修改它(我不能直接对IList类进行排序)

当修改原始列表时,应更新这些视图,保持项目排序。因此,我引入了一个SortList创建方法,该方法创建一个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>();
}

在我看来,这是一个多线程问题。我看不出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);
}否则
返回(比较);
}

如果我理解正确,您的目的只是获取列表的视图,按对象的属性排序

那么,当您可以使用LINQ
OrderBy
(或者如果您使用的是.net 2.0
List.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;
}