2008年07月09日

LINQ Except拡張メソッドの不思議

  
LINQ には、以下のようなExcept拡張メソッドがある、

public static IEnumerable<TSource> Except<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
IEqualityComparer<TSource> comparer
)


指定された IEqualityComparer を使用して値を比較し、2つのシーケンスの差集合を取り出すメソッドだ。

この Except拡張メソッドでハマった。

以下のような比較クラスを書き、そのインスタンスを引数に与えているのに、なぜか正しく動いてくれない。

class PropComparer : IEqualityComparer<PropertyInfo> {
public bool Equals(PropertyInfo x, PropertyInfo y) {
return x.Name == y.Name;
}

public int GetHashCode(PropertyInfo obj) {
return obj.GetHashCode();
}
}

Equalsメソッドにブレークポイントを設定してみたけど、止まることなく、Exceptメソッドが終わってしまう。
そんなはずはないんだけど...
しばらく悩んだ末に、書いたのが以下の明示的にインターフェースを実装したコード。

class PropComparer : IEqualityComparer<PropertyInfo> {
public bool Equals(PropertyInfo x, PropertyInfo y) {
return x.Name == y.Name;
}

public int GetHashCode(PropertyInfo obj) {
return obj.Name.GetHashCode();
}

bool IEqualityComparer<PropertyInfo>.Equals(PropertyInfo x, PropertyInfo y) {
return Equals(x, y);
}

int IEqualityComparer<PropertyInfo>.GetHashCode(PropertyInfo obj) {
return this.GetHashCode();
}
}

わけ分からないけど、こっちはちゃんと動いた。
明示的にインターフェースを実装したEqualsが呼び出されているようだ。
そういうものなのかな? いまいちどうしてなのか分からない。

普通、明示的にインターフェースを実装しなくても、インターフェース経由での呼び出しもできるはずなんだけど... ちがったっけ?

----
2007/7/10 追記

実は、PropComparerクラスに、明示的にインターフェースを実装した際に、GetHashCodeの中身が不適切だったことに気がつき、

    public int GetHashCode(PropertyInfo obj) {
return obj.Name.GetHashCode();
}


と変更している。これで正しく動くことを確認。
明示的にインターフェースを実装していなかったバージョンでは、GetHashCodeを直してのテストはしなかった。

でも、NyaRuRuさんの指摘で、はたと気がつきました。
HashCodeが非適切な値を返していたために、Equalsメソッドが呼ばれなかったんだ!
インターフェースを明示的に実装している/していないは、まったく関係ないのに、勘違いもはなはだしいです。


class PropComparer : IEqualityComparer<PropertyInfo> {
public bool Equals(PropertyInfo x, PropertyInfo y) {
return x.Name == y.Name;
}

public int GetHashCode(PropertyInfo obj) {
return obj.Name.GetHashCode();
}
}


とGetHashCodeを変更し、動かしてみたところ、あたりまえだけど、ちゃんと Exceptメソッドが動いた。


この記事へのコメント
GetHashCode の実装が,
return obj.Name.GetHashCode();

return obj.GetHashCode();

return this.GetHashCode();
のみっつが混在してますが,そのあたり大丈夫ですか?
Posted by NyaRuRu at 2008年07月09日 23:40
あっ、ほんとだ。
記事を書く際に、あとからコードの一部を手直しした時に
本来のコードとは違うコードになってました。
後で直しておきます。
GetHashCode()が正しい値になっていなかったのが原因ですね。
ご指摘ありがとうございます。

Posted by Gushwell at 2008年07月10日 09:03
 

この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/gushwell/51566221