2009-06-22 6 views
15

カスタムの比較関数を使用して組み込みのC#List.Sort関数を使用して異常な動作が発生しています。C#のList.Sort:nullオブジェクトで比較器が呼び出されています

何らかの理由により、パラメータの1つとしてnullオブジェクトを使用して、comparerクラスのCompareメソッドを呼び出すことがあります。しかし、デバッガでリストをチェックすると、コレクションにnullオブジェクトはありません。

マイ比較子クラスは次のようになります。したがって、上記のデリゲートがnullをスローします

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

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メソッドに渡すことができますxパラメータの参照例外は、mylistの要素はnullではありません。

UPDATE:はい、私はそれがnull参照の例外をスローするパラメータXであることを絶対に確信しています!

UPDATE:代わりに、フレームワークのはlist.sortメソッドを使用しての、私は(。ソート(マイリスト)すなわち新しいバブルソート())カスタムの並べ替え方法を試してみましたが、問題が去っていきました。私が推測しているように、List.Sortメソッドは何らかの理由で比較関数にnullを渡します。

+2

編集し直してください - 再現可能なものはありませんか? (btw、あなただったら - 本当に保証されていたdownvoteだったのですか?) –

+1

合意 - 問題を再現する短いしかし完全なプログラムはとても便利でしょう。私はこれがList.Sortのバグであることを非常に疑う。 –

答えて

18

この問題は< xをyは常にyの< Xを意味するものではありませんように、発生します。あなたの例では、SomePropの型の2つのインスタンスがどのように比較されているかを確認する必要があります。

これは、問題を再現する例です。ここでは、病理学的な比較関数 "compareStrings"が原因です。これはリストの初期状態に依存します。最初の順序を "C"、 "B"、 "A"に変更すると、例外はありません。

ソート関数ではこれをバグと呼ぶことはありません。単純に、比較関数が一貫している必要があります。

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では、これはエラーをスローしません。それはどうしたの? –

+2

バグではありませんが、例外が標準のヌルポインタexの代わりに "InconsistentComparisionMethodException"になる場合はいいでしょう。配列にnull値がないときは...非常に混乱します – serine

2

SomePropnullですか?

特に、文字列またはNullable<T>の値。文字列で

、それを使用する方がよいでしょう:

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

(編集)

ヌルセーフラッパーについては、あなたはComparer<T>.Defaultを使用することができます - たとえば、プロパティでリストをソートします:

+1

申し訳ありませんが、そのプロパティではなく、間違いなくパラメータxがnullです。私はこれがnull安全であることを望んでいません - nullであってはなりません。 – cbp

0

マークの答えが便利です。 NullReferenceがnullプロパティでCompareToを呼び出すことに起因すると彼は同意します。拡張クラスを必要とせずに、あなたが行うことができます:

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

SomePropTypeはデバッグ目的のためにSomeProp

0

のタイプであり、あなたはあなたのメソッドがnullセーフになりたいです。 (少なくとも、null-ref。例外を捕捉し、ハードコードされた方法でそれを処理する)。次に、デバッガを使用して、他の値がどのような順序で比較され、どのコールが成功するか失敗するかを監視します。

あなたはあなたの答えを見つけるでしょう、そして、あなたはnull安全性を取り除くことができます。

0

このコードを実行できますか?

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

私はこの問題全体に自分自身をつまずいた、そしてそれは私の入力でNaN財産に関連していたことがわかりました。ここでは、例外を生成する必要があり、最小限のテストケースです:比較関数が一貫していないとき

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

私もこの問題に遭遇してきた(null参照が私のカスタムの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)を呼び出すと同じ結果が得られることに注意してください。これは主な問題だと思います。デバッギング時に、ソートされているコレクションが空文字列に変換されていなくても、Compare()はヌル文字列ポインタを引数の1つとして渡します。

TryParseの問題を修正して実装の一貫性を確保するとすぐに、問題は解消され、比較はnullポインタが渡されなくなりました。

関連する問題