C#类型基础----对象判等
来源:互联网 发布:数据恢复软件的应用 编辑:程序博客网 时间:2024/06/06 05:41
C#类型基础----对象判等
前言
前面说了一点关于值类型和引用类型的东西,如果你能稍微有点收获,那将会是楼主的万幸!今天说一点关于对象判等的东西.因为对象复制(克隆)的一个前提条件就是:要能够知道复制前后的两个对象是否相等,所以,再战看对象赋值的内容前,有必要先了解如果进行对象判等.
正文
先定义用作示例的两个类型,他们代表一维坐标系(直线)上的地点,唯一区别是一个是引用类型class,一个是值类型struct:
public class RefPoint { public int x; public RefPoint(int x) { this.x = x; } } public struct ValPoint { public int x; public ValPoint(int x) { this.x = x; }}
首先来看引用类型对象的判等,大家知道System.object基类型中,定义了实例方法Equals(object obj),静态方法Equals(object objA,object objB),静态方法ReferenceEquals(Object objA,object objB)这三个方法来进行对象的判等.
先看看这三个方法是如何实现的:
public static bool ReferenceEquals(object objA, object objB) { return objA == objB; } public virtual bool Equals(object obj) { return InternalEquals(this,obj); } public static bool Equals(object objA, object objB) { if (objA==objB) { return true; } if (objA==null||objB==||) { return false; } return objA.Equals(objB); }
先看ReferenceEquals(object objA,object objB)方法,它实际上简单的返回objA==objB.再观察一下object.Equals()静态方法,如果任何一个对象引用为null,则总会返回false.当对象不为null时,最后调用了实例上的Equals()方法.
bool result; RefPoint rPoint1 = new RefPoint(1); RefPoint rPoint2 = rPoint1; result = (rPoint1 == rPoint2); Console.WriteLine(result); result = rPoint2.Equals(rPoint1); Console.WriteLine(result); Console.Read();
在阅读本节的时候,应该时刻在脑子里构思一个栈和一个堆,并思考着每条语句会在这两种结构上产生怎么样的效果.在这段代码中,产生的效果如下图所示:在堆上创建了一个
新的RefPoint类型的对象实例,并将它的x字段初始化为1;在栈上创建RefPoint类型的变量rPoint1,rPoint1保存了堆上这个对象的地址;而将rPoint1赋值给rPoint2时,此时并没有在堆上创建一个新的对象,而是将之前创建的对象的地址复制到了rPoint2.此时,rPoint1和rPoint2指向了堆上同一个对象.
从ReferenceEquals()这个方法名就可以看出,它判断两个引用变量是不是指向了同一个变量,如果是,那么返回true.这种相等叫做引用引用相等(rPoint1==rPoint2相当于ReferenceEquals).因为他们指向的是同一个对象,所以对rPoint1的操作将会影响rPoint2.
再看引用类型的第二种情况:
//创建新引用类型的对象,其成员的值相等. bool result; RefPoint rPoint1 = new RefPoint(1); RefPoint rPoint2 = new RefPoint(1); result=(rPoint1==rPoint2); Console.WriteLine(result); result = rPoint1.Equals(rPoint2); Console.WriteLine(result);
上面的代码在堆上创建了两个类型实例,并用同样的值初始化它们;然后将它们的地址分别赋给栈上的变量rPoint1和rPoint2.此时Equals返回了false.由此可见,对于引用类型,即使类型的实例(对象)包含的值相等,如果变量指向的是不同的对象,那么也不相等.
简单值类型判等
咱们先研究一下简单值类型,这个简单的定义是怎样的呢?如果值类型的成员仅包含值类型,那么暂且管它叫简单值类型;如果值类型的成员包含引用类型,则管它叫复杂值类型.
前面说过,值类型都会隐式地继承System.ValueType类型,而ValueType类型覆盖了基类System.Object类型的Equals()方法,在值类型上调用Equals()方法,会调用ValueType的Equals().
先看第一段代码:
//复制结构变量 bool result; ValPoint vPoint1 = new ValPoint(1); ValPoint vPoint2 = vPoint1; result=(vPoint1==vPoint2);//这里出现编译错误:不能在ValPoint上应用"=="操作符 Console.WriteLine(result); result = object.ReferenceEquals(vPoint1,vPoint2);//隐式装箱,指向了堆上的不同对象 Console.WriteLine(result);//返回false
上面的代码先在栈上创建了一个变量vPoint1,由于ValPoint是结构类型,因此变量本身已经包含了所有字段和数据.然后在栈上复制了vPoint1的一份副本给了vPoint2.如果按照前面的思维去理解,那么肯定会认为它们应该是相等的.然而,接下来试着去比较它们,就会看到,不能用”==”直接去判断,这样会返回一个编译错误.
如果调用System.Object基类的静态方法ReferenceEquals(),就会发生有意思的事情:它返回了false.为啥?看下ReferenceEquals()方法的签名就可以了,他接受的是Object类型,也就是引用类型,而当传递vPoint1和vPoint2这两个值类型的时候,会进行一个隐式的装箱,想过相当于下面的语句:
object boxPoint1=vPoint1;object boxPoint2=vPoint2;result=(boxPoint1==boxPoint2);//返回false
装箱的过程,在前面说过了,上面的操作等于在堆上创建了两个对象,对象包含的内容相同,但对象所在的地址不同.最后将对象地址分别返回给堆栈上的boxPoint1和boxPoint2变量,再去比较boxPoint1和boxPoint2是否指向同一个对象,显然不是了,所以返回了false.
复杂值类型判等
重新定义一个新的结构ValLine,它代表直线上的线段,让它的一个成员为值类型ValPoint,一个成员为引用类型RefPoint,然后作比较.
using System;using System.Collections.Generic;using System.Data.SqlClient;using System.Linq;using System.Text;using System.Threading.Tasks; namespace ConsoleApplication1{ public class RefPoint { public int x; public RefPoint(int x) { this.x = x; } } public struct ValPoint { public int x; public ValPoint(int x) { this.x = x; } } public struct ValLine { public RefPoint rPoint;//引用类型成员 public ValPoint vPoint;//值类型成员 public ValLine(RefPoint rPoint, ValPoint vPoint) { this.rPoint = rPoint; this.vPoint = vPoint; } } class Program { static void Main(string[] args) { bool result; ValPoint vPoint = new ValPoint(1); RefPoint rPoint = new RefPoint(1); ValLine line1 = new ValLine(rPoint,vPoint); ValLine line2 = line1; result = line1.Equals(line2); Console.WriteLine(result); Console.Read(); } } }
这个案例的过程要复杂很多.在开始之前,先思考一下,当写下line1.Equals(line2)时,已经进行了一个装箱的操作.如果要进一步判等,显然不能去判断变量是否引用了堆上同一个对象,这样就没有意义了,对吧,因为总是会返回false(装箱后堆上创建了两个对象).那么应该如何判断呢?对堆上对象的成员(字段)进行一对一的比较,而成员又分为两种类型,一种是值类型,一种是引用类型.对于引用类型,去判断是否引用相等;对于值类型,如果是简单值类型,那么同前面说的一样去判断;如果是复杂类型,那么当然是递归调用了;最终确定要么是引用类型要么是简单值类型.
- C#类型基础----对象判等
- .NET类型对象的判等(Equals)
- 对象判等(一)
- 对象判等(二)
- C# 判等
- C#类型基础----对象复制
- Java对象判等,重写equals方法
- JAVA中值、对象的判等
- C# 4个判等操作
- C#中的四个判等函数
- C# 字符串比较(判等)优化
- C#基础(二)对象和类型
- c#基础 之对象和类型
- 区别和认识C#中四个判等函数
- C#区别和认识四个判等函数
- 区别和认识C#中四个判等函数
- C#中四个判等函数的认识
- C#基础学习第二篇:对象和类型
- YiiBooster使用:booster.helpers.TbHtml的AjaxSubmitButton bug修复
- myBatis源码之XMLConfigBuilder
- Invalid MEX-file: caffe.mexa64 的解决方案
- 【splay】BZOJ 1503 郁闷的出纳员
- js模拟php的shuffle函数,用来打乱一维数组
- C#类型基础----对象判等
- Eclipse中10个最有用的快捷键组合
- myBatis源码之Configuration
- div+css清除浮动
- C++ 学习第三天
- jvm调优心得
- cocos2D-X源码分析之从cocos2D-X学习OpenGL(4)---混合
- 部分电脑远程连接连不上但其他可以连的解决办法
- Qt Model/View( 一)