.Net 相等性:集合类 Contains 方法详解
来源:互联网 发布:淘宝卖家处理退货申请 编辑:程序博客网 时间:2024/05/16 20:53
.Net 相等性:集合类 Contains 方法详解(一)
这些方法归根结底都可追溯到以下三个接口上(不考虑非泛型版的):
一般集合类的Contains都源自ICollection<T>,字典类的ContainsKey都源自IDictionary<TKey, TValue>。另外System.Linq.Enumerable类(.Net3.0)扩展了IEnumerable<T>接口:
Contains或ContainsKey要将输入值与集合中原有的值进行相等比较,Contains涉及到.Net中的相等性。.Net表示相等有多种方法,先看Object类:
这其中有四个相等的方法:
1 public virtual bool Equals(object obj)2 public static bool Equals(object objA, object objB)
3 public static bool ReferenceEquals(object objA, object objB)
4 public static bool operator == (object objA, object objB)
第四个是==的运算符重载,系统默认实现。这四个相等性在值类型和引用类型含义不同,要把这四个相等性的问题说明清楚也不是件容易事,大家可以去看下《Effective c#》一书,其中有对此的详细阐述,我就不要详细重复了,简单说一下在引用类型中的含义吧:
1.在引用类型中,ReferenceEquals与==含义相同,都表示引用相等(ReferenceEqual)。
2.Equals(object objA, object objB)内部最终调用Equals(object obj)方法。
3.引用类型不要去重载==运算符,这样会破坏它本来的含义。
总结起来,对引用类型可简化为两个方法,就上面的方法1和方法3,方法3不用操心,它只表示引用相等,不能修改。所以我们只关心方法1,它被标记为virtual,我们可以对它进行重写(override)。
如果定义一个新的类(没有从其它类继承),没有重写Equals(object obj),它将采用一个默认实现,先看该类:
1 class People2 {
3 public int Id { get; set; }
4 public string Name { get; set; }
5 }
我们写段代码来测试下Equals的默认实现是什么?
1 People p1 = new People { Id = 1, Name = "harry" };2 People p2 = new People { Id = 1, Name = "harry" };
3
4 bool b1 = p1 == p1;
5 bool b2 = p1.Equals(p1);
6 bool b3 = p1 == p2;
7 bool b4 = p1.Equals(p2);
我们实例化了两个People,具有相同的属性。b1、b2肯定为true,自己和自己比较嘛!再来看b3,这里使用“==”进行比较,前面我们说过“==”是“引用相等”,p1、p2是两个实例,具有不同的引用,所以b3值是false。最后看b4,b4使用了Equals(object obj),也就是前面说的方法一,People类没有重写这个方法,于是就使用了Object类中的默认实现。这个默认实现就是引用相等,即ReferenceEqual。所以b4也是false。
这个默认实现与我们的实际应用含义不相同,两个实例属性全部相同,为什么还不Equal呢。因此对于引用类型,我们应当重写其Equals方法,让它更具有实际意义。下面是一个参考实现(改编自《Effective c#》):
1 public override bool Equals(object obj)2 {
3 if (obj == null) return false;
4 if (object.ReferenceEquals(this, obj)) return true;
5 //
6 if (this.GetType() != obj.GetType()) return false;
7 //
8 return CompareMembers(obj as People);
9 }
10
11 private bool CompareMembers(People other)
12 {
13 return Id.Equals(other.Id) && Name.Equals(other.Name);
14 }
注意第六行,我们判断两个类的类型是否相同,类型不同我们认定“不相等”。(People类以后可能会有派生类,派生类即使所有属性与父类相同,也认为是不相等,因为类型不同。)
重写Equals后,再来测下上面的b4吧,这次为true了。重写后Equals更具有实际意义,如果非要比较引用相等,用“==”比较即可。
再来看一些与相等性有关的接口:
前两个比较相同,后两个不但可以比较相等还可比较谁大谁小(用于集合排序)。这次只讨论前两个。两个接口的声明如下:
1 public interface IEquatable<T>2 {
3 bool Equals(T other);
4 }
5 public interface IEqualityComparer<T>
6 {
7 bool Equals(T x, T y);
8 int GetHashCode(T obj);
9 }
IEquatable<T>接口比较简单只有一个方法Equals,我们先给People类实现了,如下:
Code1 class People : IEquatable<People>
2 {
3 public int Id { get; set; }
4 public string Name { get; set; }
5
6
7 public override bool Equals(object obj)
8 {
9 if (obj == null) return false;
10 if (object.ReferenceEquals(this, obj)) return true;
11 if (this.GetType() != obj.GetType()) return false;
12 return Equals(obj as People);
13 }
14
15 public bool Equals(People other)
16 {
17 if (other == null) return false;
18 if(this == other) return true;
19 return Id.Equals(other.Id) && Name.Equals(other.Name);
20 }
21 }
把刚才的CompareMembers方法改成了Equals。而且是从私有方法变成了公有方法,所以又加上了两行代码(注意还没有对this.Name进行空值判断)。这样一来,前面测试中的计算b4值时调用的不再是Equals(object obj)了,而是调用了Equals(People other),效率会提高一些。
接下来看第二个接口 IEqualityComparer<T>,这个接口用在何处呢?请看下图:
如上这个方法是System.Linq.Enumerabler的一个扩展方法,可以传入一个IComparer<T>作为参数。这个重载 我们直接使用的比较少,大多数情况下我们使用是Collection的Contains<T>(T item)(这个方法扩展后面还会提到)。但IEqualityComparer<T>这个接口很重要,也本文的重点。
现在有一个问题,泛型集合类的Contains方法是调用的两个Equals之中的哪个呢(如People类中,两个Equals分别在7行、15行),又与这些接口什么关系呢?
我们先看使用最频繁的泛型集合类List<T>,来看它的Contains实现:
1 public bool Contains(T item)2 {
3 if (item == null)
4 {
5 for (int j = 0; j < this._size; j++)
6 if (this._items[j] == null) return true;
7 return false;
8 }
9 EqualityComparer<T> comparer = EqualityComparer<T>.Default;
10 for (int i = 0; i < this._size; i++)
11 if (comparer.Equals(this._items[i], item))return true;
12 return false;
13 }
3~8行,如果传入是item是null,也进行了处理,遍历内部集合_items(其实是个数组,定义为T[] _items),看是否也有空值。
重点在第9行,comparer = EqualityComparer<TSource>.Default(这句代码后面会多次出现)。这里出现了一个EqualityComparer<T>类,和我们前面提到的接口IEqualityComparer<T>很像的,它们是什么关系呢。我把和它和它的派生类都找了出来,连根拔起,如下:
- .Net 相等性:集合类 Contains 方法详解
- 集合中contains方法体会
- contains与compareDocumentPosition方法详解
- contains与compareDocumentPosition方法详解
- contains与compareDocumentPosition方法详解
- List集合中的contains方法使用
- 集合相等
- .Net中的相等性比较
- .net mvc lambda表达式Contains方法
- java List集合中contains方法总是返回false
- contains( )方法
- 集合相等问题
- 集合相等问题
- 集合相等问题 oj
- sdut_java_集合相等问题
- 集合相等问题
- 集合相等问题
- 集合相等问题
- InetAddress.getLocalHost() java.net.UnknownHostException 异常
- 本地svn项目代码导入eclipse的问题
- JS保留两位小数 四舍五入函数
- linux 下使用Qt连接MySQL数据库
- img 高斯平滑
- .Net 相等性:集合类 Contains 方法详解
- 用js制作的简易图片浏览
- HDU 2544 最短路 最短路入门
- 数据迁移:从SqlServer到Oracle
- Linux用户、用户组、文件权限学习笔记
- Http头 Range、Content-Range
- 操作系统之PV操作
- 状态(state)模式以及和策略(strategy)的比较
- PlotToDevice样例程序