List<T>などでTが参照型の場合、ContainsやIndexOfは値(オブジェクト内容)ではなく、参照先を判定する仕様となっている。
これを、値(オブジェクト内容)で判定させたい場合はどうするのかというと、T型にIEquatable<T>インターフェースをインプリメントして、Equalsメソッドを定義して上げれば良い。
// ノーマルバージョン
public class Person {
public string Name { get; set; }
public string Mail { get; set; }
}
var p = new Person() { Name = "T.Sumomo", Mail = "t.sumomo@momo.com" };
var p1 = new Person() { Name = "T.Sumomo", Mail = "t.sumomo@momo.com" };
p == p1
p.Equals(p1)
List<Person> l1 = new List<Person>() { p };
l1.Contains(p1)
l1.IndexOf(p1)
// 値比較バージョン
public class Person1 : IEquatable<Person1> {
public string Name { get; set; }
public string Mail { get; set; }
// 値で比較
public bool Equals(Person1 other) {
return other?.Name == Name && other?.Mail == Mail;
}
}
var p2 = new Person1() { Name = "T.Sumomo", Mail = "t.sumomo@momo.com" };
var p3 = new Person1() { Name = "T.Sumomo", Mail = "t.sumomo@momo.com" };
p2 == p3
p2.Equals(p3)
List<Person1> l2 = new List<Person1>() { p2 };
l2.Contains(p3)
l2.IndexOf(p3)
> p == p1
false
> p.Equals(p1)
false
> l1.Contains(p1)
false
> l1.IndexOf(p1)
-1
>
> p2 == p3
false
> p2.Equals(p3)
true
> l2.Contains(p3)
true
> l2.IndexOf(p3)
0
と、このように値で判定を行う事ができる。
もちろん、内容全部を比較するのでは無く、キーとなるプロパティだけの比較とかでも問題は無い。
ちなみに、Equalsではなく、== operatorをEqualsと同じ内容にしても、ContainsやIndexOfは参照先の比較になってしまう。
まぁ、わざわざクラスに手を入れないで、LINQ使ってもいいのだけど・・・