如果有个类对象的集合,要判断一个对象是不是存在这个集合中,可以用Contains方法判断。但我们都知道这判断是引用。假设这个集合里面存在和这个对象所有的值成员的值都相等的元素,我们就认为这个对象存在于这个集合中,那么我们该如果做? 我能想到就是重写Object基类中的Equals方法,然后为这个集合添加一个扩展方法。如下:
public class Racer { public Racer(string firstName, string lastName, string country = "", int wins = 0) { this.FirstName = firstName; this.LastName = lastName; this.Country = country; this.Wins = wins; } public string FirstName { get; private set; } public string LastName { get; private set; } public string Country { get; private set; } public int Wins { get; private set; } public override bool Equals(object obj) { Racer other = obj as Racer; if (other == null) return false; return FirstName == other.FirstName && LastName == other.LastName && Country == other.Country && Wins == other.Wins; } }
List<Racer>的扩展方式:
public static bool HasItem(this List<Racer> listR, Racer racer) { return listR.Any(x => x.Equals(racer)); }
然后我们就可以调用HasItem判断,测试一下:
List<Racer> listRacer = new List<Racer>(); listRacer.Add(new Racer("xixiao", "meng","china")); listRacer.Add(new Racer("zhstar", "zhou","china")); Racer racer = new Racer("zhstar", "zhou","china"); Console.WriteLine("listRacer HasItem racer:{0}", listRacer.HasItem(racer)); Console.WriteLine("listRacer Contains racer:{0}", listRacer.Contains(racer));
运行代码代码我们发现HasItem方法和Contains方法都返回了True。这是为什么呢?跟踪调试发现,原来Contains方法也是通过我们重写的Equals方法来判断的。原来如此,于是我们可以删掉扩展方法直接使用系统的Contains方法了。那是不是Contains的判断一定就是调用基类中Equals方法呢?
我们修改一下Racer类,让它集成IEquatable接口,实现Equals方法,让它返回引用比较的结果:


public class Racer :IEquatable<Racer> { public Racer(string firstName, string lastName, string country = "", int wins = 0) { this.FirstName = firstName; this.LastName = lastName; this.Country = country; this.Wins = wins; } public string FirstName { get; private set; } public string LastName { get; private set; } public string Country { get; private set; } public int Wins { get; private set; } public override bool Equals(object obj) { Racer other = obj as Racer; if (other == null) return false; return FirstName == other.FirstName && LastName == other.LastName && Country == other.Country && Wins == other.Wins; } public bool Equals(Racer other) { return object.ReferenceEquals(this, other); } }
在按照前面那样测试一下Contains和HasItem方法,我们会发现返回的结果都是false。这是不是说明了对于Equals方法,如果实现了IEquatable接口,那么会首先使用这个接口的Equals方法,如果没有实现这个接口就会使用基类的或者重写基类的Equals方法呢?
我们再修改一下Racer类,把IEquatable接口去掉,但保留Equals方法:


public class Racer { public Racer(string firstName, string lastName, string country = "", int wins = 0) { this.FirstName = firstName; this.LastName = lastName; this.Country = country; this.Wins = wins; } public string FirstName { get; private set; } public string LastName { get; private set; } public string Country { get; private set; } public int Wins { get; private set; } public override bool Equals(object obj) { Racer other = obj as Racer; if (other == null) return false; return FirstName == other.FirstName && LastName == other.LastName && Country == other.Country && Wins == other.Wins; } public bool Equals(Racer other) { return object.ReferenceEquals(this, other); } }
再运行一下,会发现HasItem方法返回了false,但Contains方法却返回True。
到这里,其实就很明白,对集合来说,它是一个泛型的类,无法知道每个类型具体的成员,在判断一个元素或者查找一个元素的时候,首先依赖的是IEquatable接口的Equals方法,如果没有继承这个接口那么就使用基类中的Equals方法。但我们在调用对象实例里面的方法,它就会找到那个最适合被调用的方法。比如上面的例子中如果我们把扩展方法的第二个参数该成object类型,就会调用我们重写的基类的Equals方法。