台州企业建站系统,做网站运营有前景么,建设网站需要支付什么插件费用吗,娱乐手机网站开发C#类型基础Part2-对象判等 参考资料引用类型判等简单值类型判等复杂值类型判等 参考资料
《.NET之美-.NET关键技术深入解析》
引用类型判等
先定义两个类型#xff0c;它们代表直线上的一个点#xff0c;一个是引用类型class#xff0c;一个是值类型struct
public class… C#类型基础Part2-对象判等 参考资料引用类型判等简单值类型判等复杂值类型判等 参考资料
《.NET之美-.NET关键技术深入解析》
引用类型判等
先定义两个类型它们代表直线上的一个点一个是引用类型class一个是值类型struct
public class RefPoint{public int x:public RefPoint(int x){this.xx;}
}public class ValPoint{public int x:public ValPoint(int x){this.xx;}
}在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; // #1
}
public virtual bool Equals(Object obj)
{return InternalEquals(this, obj); // #2
}
public static bool Equals(Object objA, Object objB)
{if (objAobjB) { // #3return true;}if (objAnull || objBnull) {return false;}return objA.Equals(objB); // #4
}先看ReferenceEquals(Object objA,Object objB)方法它实际上简单地返回 objAobjB。再观察一下Object.Equals()静态方法如果任何一个对象引用为null则总是 返回false。当对象不为null时最后调用了实例上的Equals()方法#4。
下面一段代码
// 复制对象引用
bool result;
RefPoint rPoint1 new RefPoint(1);
RefPoint rPoint2 rPoint1;
result (rPoint1 rPoint2); // 返回 true;
Console.WriteLine(result);
result rPoint1.Equals(rPoint2); // #2 返回true;
Console.WriteLine(result);在这段代码中在堆上创建了一个新的RefPoint类型的对象实例并将它的x字段初始化为1在栈上创建RefPoint类型的变量rPoint1rPoint1保存了堆上这个对象的地址而将rPoint1赋值给rPoint2时此时并没有在堆上创建一个新的对象而是将之前创建的对象的地址复制到了rPoint2。此时rPoint1和 rPoint2指向了堆上同一个对象。
从ReferenceEquals()这个方法名就可以看出它判断两个引用变量是不是指向了同一个变量如果是那么就返回true。这种相等叫做引用相等rPoint1rPoint2等效于ReferenceEquals。因为它们指向的是同一个对象所以对rPoint1的操作将会影响rPoint2。
第二种情况
//创建新引用类型的对象其成员的值相等
RefPoint rPoint1 new RefPoint(1);
RefPoint rPoint2 new RefPoint(1);
result (rPoint1 rPoint2);
Console.WriteLine(result); // 返回 false;
result rPoint1.Equals(rPoint2);
Console.WriteLine(result); // #2 返回false
上面的代码在堆上创建了两个类型实例并用同样的值初始化它们然后将它们的地址分别赋给栈上的变量rPoint1和rPoint2。此时#2返回了false可以看到对于引用类型即使类型的实例对象包含的值相等如果变量指向的是不同的对象那么也不相等。
简单值类型判等
注意本节的标题简单值类型判等这个简单是如何定义的呢如果值类型的成员仅包含值类型那么暂且管它叫简单值类型如果值类型的成员包含引用类型则管它叫复杂值类型。 值类型都会隐式地继承自System.ValueType类型而ValueType类型覆盖了基类System.Object类型的Equals()方法在值类型上调用Equals()方法会调用ValueType的Equals()。所以先看看这个方法是什么样的依然用#number标识后面会引用的地方。
public override bool Equals (Object obj) {if (nullobj) {return false;}RuntimeType thisType (RuntimeType)this.GetType();RuntimeType thatType (RuntimeType)obj.GetType();if (thatType!thisType) { // 如果两个对象不是一个类型直接返回falsereturn false;}Object thisObj (Object)this;Object thisResult, thatResult;if (CanCompareBits(this)) // #5return FastEqualsCheck(thisObj, obj); // #6// 利用反射获取值类型所有字段FieldInfo[] thisFields thisType.GetFields(BindingFlags.Instance |BindingFlags.Public | BindingFlags.NonPublic);// 遍历字段进行字段对字段比较for (int i0; ithisFields.Length; i) {thisResult ((RtFieldInfo)thisFields[i]).InternalGetValue(thisObj,false);thatResult ((RtFieldInfo)thisFields[i]).InternalGetValue(obj, false);if (thisResult null) {if (thatResult ! null)return false;}else if (!thisResult.Equals(thatResult)) { // #7return false;}}return true;
}先来看下第一段代码
// 复制结构变量
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。如果依照前面的惯性思维去考虑那么就会认为它们应该是相等的。然而接下来试着去比较它们就会看到不能用“” 直接去判断这样会返回一个编译错误“不能在ValPoint上应用操作符”。
如果调用System.Object基类的静态方法ReferenceEquals()就会发生有意思的事情它返回了false。为什么呢看下ReferenceEquals()方法的签名就可以了它接受的是Object类型也就是引用类型而当传递vPoint1和vPoint2这两个值类型的时候会进行一个隐式的装箱效果相当于下面的语句
Object boxPoint1 vPoint1;
Object boxPoint2 vPoint2;
result (boxPoint1 boxPoint2); // 返回false
Console.WriteLine(result)装箱的过程在前面已经讲述过上面的操作等于在堆上创建了两个对象对象包含的内容相同但对象所在的地址不同。最后将对象地址分别返回给堆栈上的boxPoint1和boxPoint2变量再去比较boxPoint1和boxPoint2是否指向同一个对象显然不是了所以返回了false。
继续示例程序添加下面这段代码
result vPoint1.Equals(vPoint2); // #5 返回true; #6 返回true;
Console.WriteLine(result); // 输出true因为它们均继承自ValueType类型所以此时会调用ValueType上的Equals()方法在方法体内部#5处的CanCompareBits(this) 返回了true。CanCompareBits(this)这个方法按微软的注释意思是说如果对象的成员中存在对于堆上的引用那么返回false如果不存在返回true。按照ValPoint的定义它仅包含一个int类型的字段x自然不存在对堆上其他对象的引用所以返回了true。从#5处的名字CanCompareBits可以看出是在判断是否可以进行按位比较因此返回了true以后#6自然是进行按位比较了。
接下来对vPoint2做点改动看看会发生什么
vPoint2.x 2;
result vPoint1.Equals(vPoint2); // #5 返回true; #6 返回false;
Console.WriteLine(result);此时因为vPoint2中的int值发生了变化所以在#6处按位比较时就会返回false。
复杂值类型判等
到现在为止上面的System.ValueType.Equals()方法还没有执行到的位置就是CanCompareBits返回false以后的部分了。前面已经推算出了CanCompareBits返回false的条件值类型的成员包含引用类型现在只要实现一下就可以了。重新定义一个新的结构ValLine它代表直线上的线段让它的一个成员为值类ValPoint一个成员为引用类型RefPoint然后去作比较。
/* 结构类型 ValLine 的定义
public struct ValLine {public RefPoint rPoint; // 引用类型成员public ValPoint vPoint; // 值类型成员public Line(RefPoint rPoint, ValPoint vPoint) {this.rPoint rPoint;this.vPoint vPoint;}
}
*/
RefPoint rPoint new RefPoint(1);
ValPoint vPoint new ValPoint(1);
ValLine line1 new ValLine (rPoint, vPoint);
ValLine line2 line1;
result line1.Equals(line2); // 此时已经存在一个装箱操作调用ValueType.Equals()
Console.WriteLine(result); // 返回True这个例子的过程要复杂得多。在开始前先思考一下当写下line1.Equals(line2)时已经进行了一个装箱的操作。如果要进一步判等显然不能去判断变量是否引用了堆上同一个对象这样就没有意义了因为总是会返回false装箱后堆上创建了两个对象。那么应该如何判断呢对堆上对象的成员字段进行一对一的比较而成员又分为两种类型一种是值类型一种是引用类型。对于引用类型去判断是否引用相等对于值类型如果是简单值类型那么同前一节讲述的一样去判断如果是复杂类型那么当然是递归调用了 最终确定要么是引用类型要么是简单值类型。
好了现在看看实际的过程是不是如同我们所料想的那样为了避免频繁地拖动滚动条查看ValueType的Equals()方法这里将代码复制了部分
public override bool Equals (Object obj) {//前面略if (CanCompareBits(this)) // #5return FastEqualsCheck(thisObj, obj); // #6// 利用反射获取类型的所有字段(或者叫类型成员)FieldInfo[] thisFields thisType.GetFields(BindingFlags.Instance |BindingFlags.Public | BindingFlags.NonPublic);// 遍历字段进行比较for (int i0; ithisFields.Length; i) {thisResult ((RtFieldInfo)thisFields[i]).InternalGetValue(thisObj,false);thatResult ((RtFieldInfo)thisFields[i]).InternalGetValue(obj, false);if (thisResult null) {if (thatResult ! null)return false;}else if (!thisResult.Equals(thatResult)) { #7return false;}}return true;
}进入ValueType上的Equals()方法#5处返回了false;。进入for循环遍历字段。第一个字段是RefPoint引用类型#7处调用System.Object的Equals()方法到达#2返回true。第二个字段是ValPoint值类型#7处调用System.ValType的Equals()方法也就是当前方法本身。注意此处是递归调用。再次进入ValueType的Equals()方法因为ValPoint为简单值类型所以#5处的CanCompareBits返回了true接着#6处的FastEqualsCheck返回了true。里层Equals()方法返回true。退出for循环。外层Equals() 方法返回true