配列に対してContains()を呼ぶとIEquatable.Equals()が使われない
Enumerable.Contains<T>()
の実装は以下のようになっていて、sourceがICollection<T>
を実装していたらICollection<T>.Contains()
が呼ばれ、実装していなかったらEqualityComparer<TSource>.Default
で比較される。EqualityComparer<TSource>.Default
はsourceの要素がIEquatable<T>
を実装していたらIEquatable<T>.Equals()
で、実装していなかったらObject.Equals()
で比較する。
public static bool Contains<TSource>(this IEnumerable<TSource> source, TSource value) { ICollection<TSource> collection = source as ICollection<TSource>; if (collection != null) return collection.Contains(value); return Contains<TSource>(source, value, null); }
https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,d1530e4eed8b26b3
配列はICollection<T>
を実装しているが、IEquatable<T>.Equals()
ではなくObject.Equals()
を使って比較する。そのため、以下のようにIEquatable<T>
を実装しているのにObject.Equals()
がオーバーライドされていないと変な結果になってしまう。ガイドラインに従ってきちんとオーバーライドしよう。
List<T>
もICollection<T>
を実装しているが、そのContains()
の実装ではEqualityComparer<TSource>.Default
で比較している。Enumerable.Repeat<T>()
は単なるイテレータを返すのでICollection<T>
を実装したオブジェクトではない。つまりどちらもsourceの要素がIEquatable<T>
を実装していたらIEquatable<T>.Equals()
で比較する。
https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,521b9f7129105e15
https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,e2850cfe2b0cc87f
IEquatableについて詳しくはこちら。
ところで、配列をGetType()
するとSystem.Array
型であると分かるが、ArrayクラスはジェネリクスではなくICollection
は実装しているもののICollection<T>
は実装していないように見える。
https://referencesource.microsoft.com/#mscorlib/system/array.cs,156e066ecc4ccedf
MSDocsによると、ジェネリクス版のインターフェースの実装は実行時に使えるようになるらしい。難しい。
Single-dimensional arrays implement the System.Collections.Generic.IList
, System.Collections.Generic.ICollection , System.Collections.Generic.IEnumerable , System.Collections.Generic.IReadOnlyList and System.Collections.Generic.IReadOnlyCollection generic interfaces. The implementations are provided to arrays at run time, and as a result, the generic interfaces do not appear in the declaration syntax for the Array class.
以下のStackOverflowが詳しい。