2014年7月12日

SortedSet<T> コンストラクタのバグ (.NET4~4.5)

次のような拡張メソッドを作った際に気付いたのだが、SortedSet<T> のコンストラクタ (IEnumerable<T>, IComparer<T>) に (要素 2 個以上のコレクション, null) を渡すと例外が発生する。
引数 1 個の場合や、要素 1 個のコレクションでは例外にならないから分かりにくい。

public static SortedSet<T> ToSortedSet<T>(this IEnumerable<T> source, IComparer<T> comparer)
{
    if( source == null ) throw new ArgumentNullException("source");
    return new SortedSet<T>(source, comparer);
}
public static SortedSet<T> ToSortedSet<T>(this IEnumerable<T> source)
{
    return source.ToSortedSet(null);
}

[検証コード]

static void Main()
{
    var test1 = new SortedSet<int>(new[]{ 1 }, null);
    Console.WriteLine(test1.Count);
    var test2 = new SortedSet<int>(new[]{ 1, 2 }, null);
    Console.WriteLine(test2.Count);
}
[結果]
1

ハンドルされていない例外: System.NullReferenceException: オブジェクト参照がオブジェクト インスタンスに設定されていません。
   場所 System.Collections.Generic.SortedSet`1..ctor(IEnumerable`1 collection, IComparer`1 comparer)

   場所 Program.Main()

原因は、SortedSet<T> のソース (Date: August 15, 2008) を見ると明らかで、168行目の comparer に this. の付け忘れ…
ちなみに、似たようなクラスである HashSet<T> は問題なし。

バグを考慮した拡張メソッド

そのうち修正されることを期待しつつ、拡張メソッドは次のように修正。

public static SortedSet<T> ToSortedSet<T>(this IEnumerable<T> source, IComparer<T> comparer)
{
    if( source == null ) throw new ArgumentNullException("source");
    return new SortedSet<T>(source, comparer ?? Comparer<T>.Default;);
}
public static SortedSet<T> ToSortedSet<T>(this IEnumerable<T> source)
{
    return source.ToSortedSet(null);
}

C# 6.0 からコンストラクタの型引数を推論してくれるようになるらしいんで、こんなメソッドはいらなくなるはず。

0 件のコメント:

コメントを投稿