スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

LINQ・Except 参照型は要注意

ハマりました。

LINQのExceptというと、差集合を求めてくれる便利なメソッドなのです。例えば、

a = {1, 2, 3}
b = {1, 3, 5}

としたときに、

b.Except(a) → {5}
a.Except(b) → {2}

となります。コレクションの中身に格納されている変数が値型だったらこれOKなのです。ところが参照型だとちょっと厄介なことになります。次の例を見てみます。Listの中に参照型のint[]を格納させて差集合を求めます。EqualityComparerを使ってオブジェクトの比較の処理を与えています。継承のint[]の部分がコピペできなかったので画像でごめんなさい。

2015-05-19 150250

この結果は、

>1
>2
>4 5 6
>7 8 9 10
>続行するには何かキーを押してください . . .

となります。おかしいですね。期待した結果は、

2
7 8 9 10

となるはずです。ここでGetHashCodeとEqualsにブレークポイントを挿入してデバッグしてみると、差分計算時にGetHashCodeは読まれましたがEqualsは読まれません。GetHashCode()は罠があるようで、例えば

[Effective C#] 項目10 GetHashCode() の罠に注意すること
http://blog.masakura.jp/node/35

おそらくこの2の罠に引っかかるのではないかと。どういうことかというと、aのnew int[]{1} と bのnew int[]{1} は値は同じだけどもインスタンスが異なるので、したがってGetHashCode()の返す値も異なる。EqualityComparerはまず第1にGetHashCode()で比較して同じであればEqualsを呼ぶので、インスタンスが異なるのであればEqualsでは絶対に比較されないという罠に陥ったみたいです。

この解決法はいろいろあるとは思いますが、試しにGetHashCodeで返す値を定数にしてみます(ハッシュ比較できないので処理速度は落ちます)

        public override int GetHashCode(int[] obj)
{
return 1;
}


これで先ほどのプログラムを実行してみると、Equalsに挿入したブレークポイントも呼ばれるようになりました。結果は、

>2
>7 8 9 10
>続行するには何かキーを押してください . . .

となり、期待通りの結果になりました!Exceptは比較の処理が見えないだけに参照型のコレクションで使う場合は要注意みたいです。
スポンサーサイト
プロフィール

こしあん

Author:こしあん
(:3[____]
【TwitterID : koshian2】
【ほしい物リスト】http://goo.gl/bDtvG2

Twitter
カウンター
天気予報

-天気予報コム- -FC2-
カテゴリ
月別アーカイブ
最新記事
最新トラックバック
検索フォーム
リンク
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。