2011-01-09 70 views
0

我有一個實現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>(); 
} 

實際上,類用戶請求創建一個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

我忘了說,這與一些對象只發生值(約5%)。所以,當原始列表包含100個項目時,「排序視圖」僅包含95個項目。 – Luca 2011-01-09 11:09:43

+0

當propX.CompareTo(propY)`返回0時,你可以通過設置一個斷點來試圖調試`GenericPropertyComparer`嗎?所以你將能夠理解比較器是否工作正確,如果值實際上等於... – digEmAll 2011-01-09 11:21:19

+0

@digEmAll你明白了!我注意到IComparable.CompareTo不會調用GetHashCode!哎喲!我需要一些關於SortList IComparer函數的說明。現在我明白爲什麼每個人都試圖在SortList中重複鍵值:他們想要對鍵值進行排序,這些鍵值可以被複制(在我的情況下,「ApplicationName」視圖是重複的,但不是「ApplicationPath」)。 – Luca 2011-01-09 11:31:30

回答

1

如果我理解正確的你,你的目的只是爲了獲得一個視圖,你列出,對象的屬性來分類的。

那麼,爲什麼要使用SortedList需要唯一的鍵,當你可以很容易地使用LINQ OrderBy(或者如果你使用.net 2.0 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; 
} 

編輯:

如果你需要從你的排序集合frequeltly添加/刪除項目,也許使用SortedList會更好,但SortedList的問題是它需要唯一的密鑰,並在你的情況下,你不能保證。

無論如何,你可以使用自定義排序列表,這並不需要唯一值,看看下面的鏈接,一個簡單的實現:

Implementation of sorted IList<T> that doesn't require unique values

1

在我看來,這是一個多線程的問題。我什麼也看不見鎖定()函數是在你的代碼做什麼,但我想你會用一個標準的鎖周圍的字典接入碼有更多的運氣:

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++; 

} 

,並在除去部分相同。

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); 
}