`
jinvasshole
  • 浏览: 772836 次
文章分类
社区版块
存档分类
最新评论

区别和认识C#中四个判等函数

 
阅读更多

.Net有四个判等函数?不少人看到这个标题,会对此感到怀疑。事实上确是如此,.Net提供了ReferenceEquals、静态Equals,具体类型的Equals以及==操作符这四个判等函数。但是这四个函数之间有细微的关系,改变其中一个函数的实现会影响到其他函数的操作结果。

首先要说的是Object.ReferenceEqualsObject.Equals这两个静态函数,对于它们俩来说,是不需要进行重写的,因为它们已经完成它们所要得做的操作。

对于Object.ReferenceEquals这个静态函数,函数形势如下:

public static bool ReferenceEquals( object left, object right );

这个函数就是判断两个引用类型对象是否指向同一个地址。有此说明后,就确定了它的使用范围,即只能对于引用类型操作。那么对于任何值类型数据操作,即使是与自身的判别,都会返回false。这主要因为在调用此函数的时候,值类型数据要进行装箱操作,也就是对于如下的形式来说。

int n = 10;

Object.ReferenceEquals( n, n );

这是因为对于n这个数据装箱两次,而每次装箱后的地址有不同,而造成Object.ReferenceEquals( n, n )的结果永远为false

对于第一个判等函数来说,没有什么好扩展的,因为本身已经很好地完成了它所要做的。

对于第二个Object.Equals这个静态函数,其形式如下:

public static bool Equals( object left, object right );

按照书中对它的分析,其大致函数代码如下:

public static void Equals( object left, object right )

{

// Check object identity

if( left == right )

return true;

// both null references handled above

if( ( left == null ) || ( right == null ) )

return false;

return left.Equals( right );

}

可以说,Object.Equals这个函数完成判等操作,需要经过三个步骤,第一步是需要根据对象所属类型的==操作符的执行结果;第二步是判别是否为null,也是和第一步一样,需要根据类型的==操作符的执行结果;最后一步要使用到类型的Equals函数的执行结果。也就是说这个静态函数的返回结果,要取决于后面要提到的两个判等函数。类型是否提供相应的判等函数,成为这个函数返回结果的重要因素。

那么对于Object.Equals这个静态方法来说,虽说接受参数的类型也属于引用类型,但是不同于Object.ReferenceEquals函数,对于如下的代码,能得出正确的结果。

int n = 10;

Debug.WriteLine( string.Format( "{0}", Object.Equals( n, n ) ) );

Debug.WriteLine( string.Format( "{0}", Object.Equals( n, 10 ) ) );

这是因为在此函数中要用到具体类型的两个判等函数,不过就函数本身而言,该做的判断都做了,因此不需要去重载添加复杂的操作。

为了更好的述说剩下两个函数,先解释一下等价的意义。对于等价的意义,就是自反、对称以及传递。

所谓自反,即a == a

而对称,是a == b,则b == a

传递是 a == bb == c,则 a == c

理解等价的意义后,那么在实现类型的判等函数也要满足这个等价规则。

对于可以重载的两个判等函数,首先来介绍的是类型的Equals函数,其大致形式如下:

public override bool Equals( object right );

那么对于一个类型的Equals要做些什么操作呢,一般来说大致如下:

public class KeyData

{

private int nData;

public int Data

{

get{ return nData;}

set{ nData = value; }

}

public override bool Equals( object right )

{

//Check null

if( right == null )

return false;

//check reference equality

if( object.ReferenceEquals( this, right ) )

return true;

//check type

if( this.GetType() != right.GetType() )

return false;

//convert to current type

KeyData rightASKeyData = right as KeyData;

//check members value

return this.Data == rightASKeyData.Data;

}

}

如上增加了一个类型检查,即

if( this.GetType() != right.GetType() )

这部分,这是由于子类对象可以通过as转化成基类对象,从而造成不同类型对象可以进行判等操作,违反了等价关系。

除此外对于类型的Equals函数来,其实并没有限制类型非要属于引用类型,对于值类型也是可以重载此函数,但是我并不推荐,主要是Equals函数的参数类型是不可变的,也就是说通过此方法,值类型要经过装箱操作,而这是比较影响效率的。

而对于值类型来说,我推荐使用最后一种判等函数,即重载运算符==函数,其大致形式如下:

public static bool operator == ( KeyData left, KeyData right );

对于一个值类型而言,其的大致形式应该如下:

public struct KeyData

{

private int nData;

public int Data

{

get{ return nData;}

set{ nData = value; }

}

public static bool operator == ( KeyData left, KeyData right )

{

return left.Data == right.Data;

}

public static bool operator != ( KeyData left, KeyData right )

{

return left.Data != right.Data;

}

}

由于==操作与!=操作要同步定义,所以在定义==重载函数的时候,也要定义!=重载函数。这也是.Net在判等操作保持一致性。那么对于最后一个判等函数,这种重载运算符的方法并不适合引用类型。这就是.Net经常现象,去判断两个引用类型,不要用==,而要用某个对象的Equals函数。所以在编写自己类型的时候,要保留这种风格。

那么对于以上介绍的四种判等函数,会产生如下类似的对比表格。

操作结果取决于

适用范围

建议

Object.ReferenceEquals

两个参数对象是否属于同一个引用

引用类型

不要用它来判断值类型数据

Object.Equals

参数类型自身的判等函数

无限制

考虑装箱操作对值类型数据产生的影响

类型的Equals

类型重载函数

无限制

考虑装箱操作对值类型数据产生的影响

类型的==重载

类型重载函数

无限制

不要在引用类型中重载此运算符

那么在编写类型判等函数的时候,要注意些什么呢,给出如下几点建议。

首先,要判断当前定义的类型是否具有判等的意义;

其次,定义类型的判等函数要满足等价规则;

最后一点,值类型最好不要重载定义Equals函数,而引用类型最好不要重载定义==操作符。

对于string这个引用类型是非常特殊一个引用类型。
它有两点特殊的地方。
第一点对象分配的特殊。
例如:
string str1 = "abcd";
string str2 = "abcd";
那么.net在分配string类型的时候,先查看当前string类型列表是否有相同的,如果有的话,直接返回其的引用,否则重新分配。

第二点对象引用操作的特殊,可以说不同于真正意义上的引用操作。
例如:
string str1 = "abcd";
string str2 = str1;
str2 = "efgh";// str1 is still "abcd" here
当对于一个新的string类型是原有对象引用的时候,这点和一般的引用类型一样,但是当新的对象发生变化的时候,要重新分配一个新的地方,然后修改对象指向。

因此对于string操作的时候,尤其发生变化的时候,会显得比较慢,因为其牵扯到内存地址的变化。
对于数据量比较大的字符操作时候,使用StringBuilder来说效率会提升很高。

分享到:
评论

相关推荐

    C#微软培训资料

    10.3 构造函数和析构函数 .119 10.4 小 结 .122 第十一章 方 法 .124 11.1 方法的声明.124 11.2 方法中的参数.125 11.3 静态和非静态的方法.129 11.4 方法的重载.130 11.5 操作符重载.134 11.6 小 ...

    C#编程经验技巧宝典

    88 <br>0136 如何进行文本加密与解密 88 <br>0137 如何区别0、空字符串、Null、Empty和Nothing 89 <br>0138 从字符串中分离文件路径、文件名及扩展名 89 <br>0139 如何批量替换某一类字符串 89...

    Visual.C#2010从入门到精通

    每章讲述一个主题,在展示知识点的同时,提供具体的演示实例,最后两章详细讲解了两个综合实例,以使读者对Visual C++编程技术有整体的认识。 目 录 第一篇 Visual C++基础 第1章 Visual C++集成开发环境 2 1.1 ...

    asp.net知识库

    C#中 const 和 readonly 的区别 利用自定义属性,定义枚举值的详细文本 Web标准和ASP.NET - 第一部分 XHTML介绍 在ASP.NET页面中推荐使用覆写(Override)而不是事件处理(Event Handler) 常用编码工具类,支持base...

    十天学会ASP.net--我认为ASP.NET比ASP难很多,希望大家做好准备

    这样ASP.NET就有四种组合:VB+ACCESS,VB+SQL,C#+ACCESS,C#+SQL,本教程会使用C#+ACCESS,兼顾四种来写,让大家对ASP.NET有一个全面的认识。虽然说学习ASP.NET不需要任何ASP基础,但是我觉得如果大家ASP不会,还是...

    精通sql结构化查询语句

    讲解SQL语言的应用,提供了近500个曲型应用,读者可以随查随用,深入讲解SQL语言的各种查询语句,详细介绍数据库设计及管理,详细讲解存储过程、解发器和游标等知识,讲解了SQL语言在高级语言中的具体应用。...

    java面试题

    答:Servlet与CGI的区别在于Servlet处于服务器进程中,它通过多线程方式允许其service方法,一个实例可以服务于多个请求,并且其实例一般不会被销毁,而CGI对每个请求都产生新的进程,服务完后就销毁,所以效率上...

    网络安全实验报告完整.doc

    工具和语 言不限,可以直接调用现有模块,认证和加密不限) 三、实验结果 实验一:本通信系统为用C#语言编写的P2P方式通信的系统,其具体功能如下: 1、识别本机IP地址并赋值到文本框中,产生一个随机数作为端口号,...

    数据库课程设计设计指导书-最新.pdf

    单表查询以及多表查询:在建立的基本表中使用 select 基本格式和常用数据库函数; 设计合适的视图,实现多表间查询。 4.触发器:设计若干触发器,实现表间关联字段修改时的完整性问题。 5.存储过程:设计若干多表...

    ASPNET35开发大全第一章

    3.2.4 构造函数和析构函数 3.3 对象的生命周期 3.3.1 类成员的访问 3.3.2 类的类型 3.3.3 .NET的垃圾回收机制 3.4 使用命名空间 3.4.1 为什么要用命名空间 3.4.2 创建命名空间 3.4.3 分层设计中使用命名空间 3.5 类...

    ASP.NET 3.5 开发大全word课件

    这是整部学习资料 由于太大第一章免费供应给大家 在我的上传资源中 如果觉得还不过希望大家给个好评 当然具体本书的作者就不深究了把! 第1章 认识ASP.NET 3.5 1.1 什么是ASP.NET 1.1.1 .NET历史与展望 1.1.2 ASP...

    ASP.NET 3.5 开发大全

    3.2.4 构造函数和析构函数 3.3 对象的生命周期 3.3.1 类成员的访问 3.3.2 类的类型 3.3.3 .NET的垃圾回收机制 3.4 使用命名空间 3.4.1 为什么要用命名空间 3.4.2 创建命名空间 3.4.3 分层设计中使用命名空间 3.5 类...

    ASP.NET 3.5 开发大全1-5

    3.2.4 构造函数和析构函数 3.3 对象的生命周期 3.3.1 类成员的访问 3.3.2 类的类型 3.3.3 .NET的垃圾回收机制 3.4 使用命名空间 3.4.1 为什么要用命名空间 3.4.2 创建命名空间 3.4.3 分层设计中使用命名空间 3.5 类...

    二十三种设计模式【PDF版】

    整体结构和一些主要职责(如数据库操作 事务跟踪 安全等),剩余的就是变化的东西,针对这个领域中具体应用产生的具体不同 的变化需求,而这些变化东西就是 J2EE 程序员所要做的。 由此可见,设计模式和 J2EE 在思想...

    ASP.NET3.5从入门到精通

    3.2.4 构造函数和析构函数 3.3 对象的生命周期 3.3.1 类成员的访问 3.3.2 类的类型 3.3.3 .NET 的垃圾回收机制 3.4 使用命名空间 3.4.1 为什么要用命名空间 3.4.2 创建命名空间 3.4.3 分层设计中使用命名空间 3.5 类...

    ASP.NET 3.5 开发大全11-15

    3.2.4 构造函数和析构函数 3.3 对象的生命周期 3.3.1 类成员的访问 3.3.2 类的类型 3.3.3 .NET的垃圾回收机制 3.4 使用命名空间 3.4.1 为什么要用命名空间 3.4.2 创建命名空间 3.4.3 分层设计中使用命名空间 3.5 类...

Global site tag (gtag.js) - Google Analytics