2009-06-22 63 views
15

我得到奇怪的行爲使用內置的C#List.Sort函數與自定義比較器。List.Sort在C#中:使用空對象調用比較器

由於某些原因,它有時會將一個空對象作爲參數之一調用comparer類的Compare方法。但是,如果我使用調試器檢查列表,則集合中沒有空對象。

我的比較器類看起來是這樣的:

public class DelegateToComparer<T> : IComparer<T> 
{ 
    private readonly Func<T,T,int> _comparer; 

    public int Compare(T x, T y) 
    { 
     return _comparer(x, y); 
    } 

    public DelegateToComparer(Func<T, T, int> comparer) 
    { 
     _comparer = comparer; 
    } 
} 

這允許委託傳遞給List.Sort方法,像這樣:

mylist.Sort(new DelegateToComparer<MyClass>(
    (x, y) => { 
     return x.SomeProp.CompareTo(y.SomeProp); 
    }); 

所以上述委託將引發空即使沒有mylist的元素爲空,參數x參數的例外。

更新:是的,我絕對相信它是參數x引發空引用異常!

UPDATE:而不是使用框架的List.Sort的方法,我想自定義排序方法(即新冒泡()排序(MYLIST))和問題走了。正如我懷疑的那樣,由於某種原因,List.Sort方法將null傳遞給比較器。

+2

重新編輯 - 我不認爲你有任何可重複的,我們可以看看? (順便說一句,如果它是你 - 是一個downvote真的保證?) – 2009-06-23 06:57:17

+1

同意 - 一個簡短但完整的程序再現問題將非常方便。我非常懷疑這是List.Sort中的一個錯誤。 – 2009-06-23 10:00:08

回答

18

此問題。在你的例子中,你應該檢查如何比較兩個SomeProp類型的實例。

下面是一個重現問題的示例。在這裏,它是由病理比較函數「compareStrings」引起的。它取決於列表的初始狀態:如果將初始順序更改爲「C」,「B」,「A」,則沒有例外。

我不會把這稱爲Sort函數中的一個錯誤 - 它只是一個要求比較函數是一致的。

using System.Collections.Generic; 

class Program 
{ 
    static void Main() 
    { 
     var letters = new List<string>{"B","C","A"}; 

     letters.Sort(CompareStrings); 
    } 

    private static int CompareStrings(string l, string r) 
    { 
     if (l == "B") 
      return -1; 

     return l.CompareTo(r); 
    } 
} 
+0

在VB.NET中,這不會引發錯誤。這有多奇怪? – 2014-09-25 07:11:56

+2

不是一個錯誤,但如果異常是「InconsistentComparisionMethodException」而不是標準的空指針ex,那將會很好。當數組中沒有空值時......非常混亂 – serine 2015-02-18 15:04:20

2

你確定問題不在於SomePropnull

特別是用字符串或Nullable<T>的值。

用繩子,倒不如使用:

list.Sort((x, y) => string.Compare(x.SomeProp, y.SomeProp)); 

(編輯)

對於空安全的包裝,你可以使用Comparer<T>.Default - 例如,通過一個屬性來排序列表:

using System; 
using System.Collections.Generic; 
public static class ListExt { 
    public static void Sort<TSource, TValue>(
      this List<TSource> list, 
      Func<TSource, TValue> selector) { 
     if (list == null) throw new ArgumentNullException("list"); 
     if (selector == null) throw new ArgumentNullException("selector"); 
     var comparer = Comparer<TValue>.Default; 
     list.Sort((x,y) => comparer.Compare(selector(x), selector(y))); 
    } 
} 
class SomeType { 
    public override string ToString() { return SomeProp; } 
    public string SomeProp { get; set; } 
    static void Main() { 
     var list = new List<SomeType> { 
      new SomeType { SomeProp = "def"}, 
      new SomeType { SomeProp = null}, 
      new SomeType { SomeProp = "abc"}, 
      new SomeType { SomeProp = "ghi"}, 
     }; 
     list.Sort(x => x.SomeProp); 
     list.ForEach(Console.WriteLine); 
    } 
} 
+1

對不起,它肯定是參數x是null,而不是它的屬性。我不希望這是無效的 - 它不應該爲空。 – cbp 2009-06-23 00:32:06

0

Marc的回答很有用。我同意他的看法,NullReference是由於在一個null屬性上調用了CompareTo。無需擴展類,你可以這樣做:

mylist.Sort((x, y) => 
     (Comparer<SomePropType>.Default.Compare(x.SomeProp, y.SomeProp))); 

其中SomePropType是SomeProp

0

的類型,出於調試的目的,你希望你的方法是空安全。 (或者至少捕獲null-ref。異常,並以某種硬編碼方式處理它)。然後,使用調試器來觀察比較哪些值,按什麼順序,以及哪些調用成功或失敗。

然後你會找到你的答案,然後你可以刪除零安全。

0

你可以運行此代碼...

mylst.Sort((i, j) => 
       { 
        Debug.Assert(i.SomeProp != null && j.SomeProp != null); 
        return i.SomeProp.CompareTo(j.SomeProp); 
       } 
     ); 
0

我碰到這個問題絆倒自己,並發現它是有關我的輸入NaN屬性。下面是應該產生異常的最小的測試案例:當比較功能並不一致,使得x < Y不總是意味着Ÿ< x將出現

public class C { 

    double v; 

    public static void Main() { 
     var test = 
      new List<C> { new C { v = 0d }, 
          new C { v = Double.NaN }, 
          new C { v = 1d } }; 
     test.Sort((d1, d2) => (int)(d1.v - d2.v)); 
    } 

} 
2

我也有遇到過這個問題(空引用傳遞給我的自定義IComparer實現),最後發現該問題是由於使用不一致的比較功能。

這是我最初的IComparer實現:

public class NumericStringComparer : IComparer<String> 
{ 
    public int Compare(string x, string y) 
    { 
     float xNumber, yNumber; 
     if (!float.TryParse(x, out xNumber)) 
     { 
      return -1; 
     } 
     if (!float.TryParse(y, out yNumber)) 
     { 
      return -1; 
     } 
     if (xNumber == yNumber) 
     { 
      return 0; 
     } 
     else 
     { 
      return (xNumber > yNumber) ? 1 : -1; 
     } 
    } 
} 

此代碼中的錯誤是比較會返回-1每當值中的一個不能被正確解析(在我的情況下,它是由於錯誤地格式化字符串表示的數值,所以TryParse總是失敗)。

請注意,如果x和y的格式不正確(因此TryParse在兩者上都失敗),調用Compare(x,y)和Compare(y,x)會得到相同的結果:-1。我認爲這是主要問題。在調試時,即使被排序的集合沒有包含空字符串,Compare()也會在某個點傳遞空字符串指針作爲其參數之一。

只要我修復了TryParse問題並確保了我的實現的一致性,問題就消失了,並且Compare不再傳遞空指針。