100165822.htm

来自「C#高级编程(第三版),顶死你们。。 。up」· HTM 代码 · 共 94 行 · 第 1/2 页

HTM
94
字号
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">对象相等比较的机制对于引用类型</span><span lang="EN-US">(</span><span style="FONT-FAMILY: 宋体">类的实例</span><span lang="EN-US">)</span><span style="FONT-FAMILY: 宋体">的比较和值类型</span><span lang="EN-US">(</span><span style="FONT-FAMILY: 宋体">基本数据类型,结构或枚举的实例</span><span lang="EN-US">)</span><span style="FONT-FAMILY: 宋体">的比较来说是不同的。下面分别介绍引用类型和值类型的相等比较。</span></p>
<h3 style="MARGIN: 8.15pt 0cm"><span lang="EN-US">5.3.1&nbsp; </span><span style="FONT-FAMILY: 黑体">引用类型的相等比较</span></h3>
<p class="MsoNormal"><span lang="EN-US">System.Object</span><span style="FONT-FAMILY: 宋体">的一个初看上去令人惊讶的方面是它定义了</span><span lang="EN-US">3</span><span style="FONT-FAMILY: 宋体">个不同的方法,来比较对象的相等性:</span><span lang="EN-US">ReferenceEquals()</span><span style="FONT-FAMILY: 宋体">和</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">的两个版本。再加上比较运算符</span><span lang="EN-US">==</span><span style="FONT-FAMILY: 宋体">,实际上有</span><span lang="EN-US">4</span><span style="FONT-FAMILY: 宋体">种进行相等比较的方式。这些方法有一些微妙的区别,下面就介绍这些方法。</span></p>
<h3 style="MARGIN: 8.15pt 0cm"><span lang="EN-US">5.3.2&nbsp; ReferenceEquals()</span><span style="FONT-FAMILY: 黑体">方法</span></h3>
<p class="MsoNormal"><span lang="EN-US">ReferenceEquals()</span><span style="FONT-FAMILY: 宋体">是一个静态方法,测试两个引用是否指向类的同一个实例,特别是两个引用是否包含内存中的相同地址。作为静态方法,它不能重写,所以只能使用</span><span lang="EN-US">System.Object</span><span style="FONT-FAMILY: 宋体">实现。如果提供的两个引用指向同一个对象实例,</span><span lang="EN-US">ReferenceEquals()</span><span style="FONT-FAMILY: 宋体">总是返回</span><span lang="EN-US">true</span><span style="FONT-FAMILY: 宋体">,否则就返回</span><span lang="EN-US">false</span><span style="FONT-FAMILY: 宋体">。但是它认为</span><span lang="EN-US">null</span><span style="FONT-FAMILY: 宋体">等于</span><span lang="EN-US">null</span><span style="FONT-FAMILY: 宋体">:</span></p>
<p class="2" style="MARGIN: 8.15pt 0cm 0pt 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">SomeClass x, y;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">x = new SomeClass();</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">y = new SomeClass();</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">bool B1 = ReferenceEquals(null, null);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //return true</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">bool B2 = ReferenceEquals(null, x);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; //return false</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">bool B3 = ReferenceEquals(x, y);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; //return false because x and y </span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; //point to different objects</span></p>
<h3 style="MARGIN: 8.15pt 0cm"><span lang="EN-US">5.3.3&nbsp; </span><span style="FONT-FAMILY: 黑体">虚拟的</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 黑体">方法</span></h3>
<p class="MsoNormal"><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">虚拟版本的</span><span lang="EN-US">System.Object</span><span style="FONT-FAMILY: 宋体">实现也比较引用。但因为这个方法是虚拟的,所以可以在自己的类中重写它,按值来比较对象。特别是如果希望类的实例用作字典中的键,就需要重写这个方法,以比较值,否则,根据重写</span><span lang="EN-US">Object.GetHashCode()</span><span style="FONT-FAMILY: 宋体">的方式,包含对象的字典类要么不工作,要么工作的效率非常低。在重写</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">方法时要注意,重写的代码不会抛出异常。这是因为如果抛出异常,字典类就会出问题,可能会包含其他在内部调用这个方法的</span><span lang="EN-US">.NET</span><span style="FONT-FAMILY: 宋体">基类。</span></p>
<h3 style="MARGIN: 8.15pt 0cm"><span lang="EN-US">5.3.4&nbsp; </span><span style="FONT-FAMILY: 黑体">静态的</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 黑体">方法</span></h3>
<p class="MsoNormal"><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">的静态版本与其虚拟实例版本的作用相同,其区别是静态版本带有两个参数,并对它们进行相等比较。这个方法可以处理两个对象中有一个是</span><span lang="EN-US">null</span><span style="FONT-FAMILY: 宋体">的情况,因此,如果一个对象可能是</span><span lang="EN-US">null</span><span style="FONT-FAMILY: 宋体">,这个方法就提供了禁止抛出异常的额外保护。静态重载首先要检查它传送的引用是否为</span><span lang="EN-US">null</span><span style="FONT-FAMILY: 宋体">。如果它们都是</span><span lang="EN-US">null</span><span style="FONT-FAMILY: 宋体">,就返回</span><span lang="EN-US">false</span><span style="FONT-FAMILY: 宋体">。如果两个引用都不指向</span><span lang="EN-US">null</span><span style="FONT-FAMILY: 宋体">,它就调用</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">的虚拟实例版本。这表示在重写</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">的实例版本时,其效果相当于也重写了静态版本。</span></p>
<h3 style="MARGIN: 8.15pt 0cm"><span lang="EN-US">5.3.5&nbsp; </span><span style="FONT-FAMILY: 黑体">比较运算符==</span></h3>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">最好将比较运算符看作是严格值比较和严格引用比较之间的中间选项。在大多数情况下,下面的代码:</span></p>
<p class="2" style="MARGIN: 8.15pt 0cm 8.15pt 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">bool b = (x == y);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //x, y object references</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">表示比较引用。但是,如果把一些类看作值,其含义就会比较直观。在这些情况下,最好重写比较运算符,以执行值的比较。后面将讨论运算符的重载,但显然它的一个例子是</span><span lang="EN-US">System.String</span><span style="FONT-FAMILY: 宋体">类,</span><span lang="EN-US">Microsoft</span><span style="FONT-FAMILY: 宋体">重写了这个运算符,比较字符串的内容,而不是它们的引用。</span></p>
<h3 style="MARGIN: 8.15pt 0cm"><span lang="EN-US">5.3.6&nbsp; </span><span style="FONT-FAMILY: 黑体">值类型的相等比较</span></h3>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">在进行值类型的相等比较时,采用与引用类型相同的规则:</span><span lang="EN-US">ReferenceEquals()</span><span style="FONT-FAMILY: 宋体">用于比较引用,</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">用于比较值,比较运算符可以看作是一个中间项。但最大的区别是值类型需要装箱,才能把它们转换为引用,才能对它们执行方法。另外,</span><span lang="EN-US">Microsoft</span><span style="FONT-FAMILY: 宋体">已经在</span><span lang="EN-US">System.ValueType</span><span style="FONT-FAMILY: 宋体">类中重载了实例方法</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">,以便对值类型进行合适的相等测试。如果调用</span><span lang="EN-US">sA.Equals(sB)</span><span style="FONT-FAMILY: 宋体">,其中</span><span lang="EN-US">sA</span><span style="FONT-FAMILY: 宋体">和</span><span lang="EN-US">sB</span><span style="FONT-FAMILY: 宋体">是某个结构的实例,则根据</span><span lang="EN-US">sA</span><span style="FONT-FAMILY: 宋体">和</span><span lang="EN-US">sB</span><span style="FONT-FAMILY: 宋体">是否在其所有的字段中包含相同的值,而返回</span><span lang="EN-US">true</span><span style="FONT-FAMILY: 宋体">或</span><span lang="EN-US">false</span><span style="FONT-FAMILY: 宋体">。另一方面,在默认情况下,可以对自己的结构使用</span><span lang="EN-US">==</span><span style="FONT-FAMILY: 宋体">的未重载版本。在表达式中使用</span><span lang="EN-US">(sA==sB)</span><span style="FONT-FAMILY: 宋体">会导致一个编译错误,除非在代码中为结构提供了</span><span lang="EN-US">==</span><span style="FONT-FAMILY: 宋体">的重载版本。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">另外,</span><span lang="EN-US">ReferenceEquals()</span><span style="FONT-FAMILY: 宋体">在应用于值类型时,总是返回</span><span lang="EN-US">false</span><span style="FONT-FAMILY: 宋体">,因为为了调用这个方法,值类型需要装箱到对象中。即使使用下面的代码:</span></p>
<p class="2" style="MARGIN: 8.15pt 0cm 8.15pt 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">bool b = ReferenceEquals(v, v);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //v is a variable of some value type</span></p>
<p class="MsoNormal" style="TEXT-INDENT: 0cm"><span style="FONT-FAMILY: 宋体">也会返回</span><span lang="EN-US">false</span><span style="FONT-FAMILY: 宋体">,因为在转换每个参数时,</span><span lang="EN-US">v</span><span style="FONT-FAMILY: 宋体">都会被单独</span><span style="FONT-FAMILY: 宋体">装箱,这意味着会得到不同的引用。调用</span><span lang="EN-US">ReferenceEquals()</span><span style="FONT-FAMILY: 宋体">来比较值类型实际上没有什么意义。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">尽管</span><span lang="EN-US">System.ValueType</span><span style="FONT-FAMILY: 宋体">提供的</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">默认重载肯定足以应付绝大多数自定义的结构,但仍可以为自己的结构重写它,以提高性能。另外,如果值类型包含作为字段的引用类型,就需要重写</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">,以便为这些字段提供合适的语义,因为</span><span lang="EN-US">Equals()</span><span style="FONT-FAMILY: 宋体">的默认重写仅比较它们的地址。</span></p></div>
                <!-- page -->
                <div class="page" style="text-align: center">
                    <a href="100165821.htm">上一页</a>&nbsp;&nbsp;&nbsp;<a href="index.html">首页</a>&nbsp;&nbsp;&nbsp;<a href="100165823.htm">下一页</a>
                </div>
              
                <div style="margin: 0px auto; width: 700px; border: solid 1px #0b5f98;">
                    <div style="float: left; width: 16px; background-color: #0b5f98; color: White; padding: 1px;">
                        图书导读
                    </div>
                    <div style="float: right; width: 670px; text-align: left; line-height: 16pt; padding-left: 2px">
                        <!--导读-->
                        <h1 id="divCurrentNode2" style="color: #b83507; width: 100%; text-align: left; font-size: 12px; padding-left: 2px">当前章节:<a href='100165822.htm'><font color='red'>5.3 对象的相等比较</font></a></h1>
                        <div id="divRealteNod2" style="padding-left: 2px">
                        <div style='float:left;width:49%'>·<a href='100165819.htm'>4.4 接口</a></div><div style='float:right;width:49%'>·<a href='100165820.htm'>5.1 运算符</a></div><div style='float:left;width:49%'>·<a href='100165821.htm'>5.2 类型的安全性</a></div><div style='float:right;width:49%'>·<a href='100165823.htm'>5.4 运算符重载</a></div><div style='float:left;width:49%'>·<a href='100165824.htm'>5.5 用户定义的数据类型转换</a></div><div style='float:right;width:49%'>·<a href='100165825.htm'>5.6 小结 </a></div></div>
                    </div>
                </div>
                </div></div>
   
</body>
</html>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?