• 区别和认识四个判等函数


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

    首先要说的是Object.ReferenceEquals和Object.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 == b,b == 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函数。所以在编写自己类型的时候,要保留这种风格。

  • 相关阅读:
    unexpected inconsistency;run fsck manually esxi断电后虚拟机启动故障
    centos 安装mysql 5.7
    centos 7 卸载mysql
    centos7 在线安装mysql5.6,客户端远程连接mysql
    ubuntu 14.04配置ip和dns
    centos7 上搭建mqtt服务
    windows eclipse IDE打开当前类所在文件路径
    git 在非空文件夹clone新项目
    eclipse中java build path下 allow output folders for source folders 无法勾选,该如何解决 eclipse中java build path下 allow output folders for source folders 无法勾选,
    Eclipse Kepler中配置JadClipse
  • 原文地址:https://www.cnblogs.com/elzero/p/817955.html
Copyright © 2020-2023  润新知