C#关于重写的一些问题

来源:互联网 发布:量化数据考核指标 编辑:程序博客网 时间:2024/04/30 02:25

C# 重写Equals()方法

最近在学习C#时遇到了关于重写Equals()方法的内容,感觉是一个很重要的内容,在这里做一个学习笔记。第一次使用Markdown写文章,如有疏忽,请见谅。

目录

  • C 重写Equals方法
    • 目录
    • 重写 GetHashCode方法
      • Hash Code是什么
      • 为什么重写Equals方法必须要GetHashCode
      • HashCode的实现原则2
      • Example
    • 重写Equals方法
      • ReferenceEquals方法和Equals方法
        • ReferenceEquals方法
          • Example
        • ObjectEquals方法3
      • 重写Equals方法的步骤4
    • 操作符重载
      • 重载比较操作符 和
        • 注意事项
      • 二元操作符和-的重载
      • 重载true和false
        • Example

重写 GetHashCode()方法

Hash Code是什么

 散列码(Hash Code)的作用是生成和对象相对应的值。 散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值的列表。散列值通常用来代表一个短的随机字母和数字组成的字符串。——[中文维基](https://zh.wikipedia.org/wiki/%E6%95%A3%E5%88%97%E5%87%BD%E6%95%B8)

为什么重写Equals()方法必须要GetHashCode()

     相同的对象的散列码必然是相同的,但是反之不然,散列码相同的两个对象不一定是相同的,有可能对象的数目比散列码的数目,造成了一对多的映射,因此在C#中不要用散列码作为判断两个对象相同的唯一依据,但是可以用作判断两个对象不相同的依据。如果类不改写GetHashCode()    对于引用类型,只有当引用相同时,才会返回相同的散列码。很多时候我们会遇到这样的情况,例化了两个一模一样的实例,但是他们的引用不一样的,他们的散列码就不一样。    对于值类型,这种情况会好很多,两个对象值相同,类型相同,就会返回相同的散列码。(PS:相同的值但是类型不同,散列码会不一样)1

HashCode的实现原则2

 1. 相等的对象必须有相等的散列码(**Must**) 2. 特定在对象的生存期内,GetHashCode()始终要返回相同的值(__Must__) 3. GetHashCode()不允许引发任何异常,方法总是可以成功的返回一个值(**Must**) 4. 散列码尽可能唯一(**To the best**) 5. 散列码尽可能在int范围内平均分布(**To the best**) 6. GetHashCode()尽可能优化,因为会非常频繁的调用它,在实际的Equals()方法中,用GetHashCode()方法首先判断两个对象是否相同(**To the Best**) 7. 两个对象的细微差异应当造成散列码值得极大差异。理想情况下1位的差异应造成散列码16位的不同。通常用按位异或操作生成散列码。(**To the best**) 8. 攻击者应当难以伪造具有特定散列码的对象,即使知道了散列码,很难反推出对象。(**For safety**)

Example

public class Person{    public Person(string name,string sex,int age,int id)    {        Name = name;        Sex = sex;        Age = age;        Id = id;    }    public string Name {get;set;}    public string Sex {get;set;}    public int Age {get;set;}    public readonly id;    public override int GetHashCode()    {        int hashCode = Name.GetHashCode();        hashCode ^= Sex.GetHashCode();        hashCode ^= Age.GetHashCode();        hashCode ^= (int)Id^(int)(Id >> 32);        return hashCode;    }    //Other code}

重写Equals()方法

ReferenceEquals()方法和Equals()方法

ReferenceEquals()方法

1. 不能被重写2. 当对象引用相同时,返回true,否则返回false3. 值类型调用该方法永远返回false。     值类型调用该方法时先进行装箱,之后引用就不一样的
Example
         int  a = 5;         int b = 5;         if(a.ReferenceEquals(b))         {             Console.WriteLine("a({0} DOES reference equal to b({1}))",a,b);         }         else         {             Console.WriteLine("a({0}) does NOT reference equal to b({1})",a,b);         }**//运行结果**//a(5) does NOT reference equal to b(5)

Object.Equals()方法3

1. 判断两个对象是否相等,相等则返回true,否则返回false2. 对于引用类型的对象,调用ReferenceEquals()方法,返回值同ReferenceEquals()方法3. 对于值类型的对象,数据类型和数据相同则返回true,反之返回false4. 基于第二点,我们有必要重写Object.Equals()方法

重写Equals()方法的步骤4

重写Object.Equals()

1. 检查是否为null,如果为null直接返回false2. 如果是引用类型就检查引用类型是否相等,引用相等直接返回true3. 检查数据类型是否相同,不同则返回false4. 指定一个具体的辅助方法,将要比较的类型传入方法

重写Equals()

5. 检查散列码是否相等,如果散列码都不相等,就没有必要再进行接下来的比较,直接返回false即可6. 如果基类重写了Equals()方法,先检查base.Equals(),如果base.Equals()返回false,直接返回false7. 比较每一个标识字段,判断是否相等。8. 重写GetHashCode()方法9. 重写==和!=

操作符重载

重载比较操作符 == 和 !=

    一旦重写了Equals()可能出现这样的情况,比较对象执行Equals()返回比较结果为true,而==比较返回结果为false,因为==对于引用类型默认是执行引用相等比较的.因此有必要改写==和!=操作符,实际上==和!=必须要同时改写,否则编译器会报错。实际设计中将==和!=委托给Equals()即可,步骤如下先重载== (语法:public static override operator ==())1. 检查要比较的对象是否为null,若同时为null,返回true,若只有一个为null返回false。都不为null进行下一步。2. 调用Equals()对对象进行比较3. 重载!=,即返回!(leftHandSide == rightHandSide)

注意事项

1. ==和!=操作符重载过程中避免引发异常,要总是能返回一个值2. 重写时检查对象是否为空一定要避免递归调用==和!=,应该使用ReferenceEquals(null)方法来检查是否为空,否则死循环

**二元操作符+和-的重载

    对于某些类的实例可能需要对其进行相加,而代码中没有定义对其的+操作,因此自己重写操作符即可,要求其中至少有一个是包容类型(下面代码中Class1Name和Class2Name至少有一个要和ClassName相同)

语法如下

    public static ClassName operator +(Class1Name item1,Class2Name item2)    public static ClsaaName operator -(Cladd2Name item1,Class2Name item2)
    +,-,*,/,&,|,^,<<,>>操作符都能够被重载为二元静态方法,其中至少有一个参数的类型要是包容类型    C#不支持对赋值操作符进行重载, 然而重载了+,-,*,/,&,|,^,<<,>>操作符就自动重载了赋值操作符和二元操作符的结合

重载true和false

    重载true和false是一个很奇怪的东西,一开始我很不能理解为什么要重载true和false,阅读了MSDN上关于[true Operator](https://msdn.microsoft.com/en-us/library/6x6y6z4d.aspx)的内容,得到了下面的一些理解。    C#2.0之后支持可空的值类型,比如int? a = null,这样的语法是允许的,那么这个时候问题来了,a != b和!(a == b)的逻辑是不等价的,因为他们有可能是空,这个时候就需要自己定义true和false操作符,同时自己定义与或非运算。    同时将==和!=一同重载    重载==和!=就要重载Equals()方法和GetHashCode()方法,否则报warning

Example

//@C#public struct TriBool//三值逻辑,允许可为null的值类型逻辑判断    {        private sbyte _Value;        public TriBool(int  value)        {            _Value = (sbyte)value;        }        public sbyte Value        {            get            {                return _Value;            }        }        public static readonly TriBool Null = new TriBool(0);        public static readonly TriBool False = new TriBool(-1);        public static readonly TriBool True = new TriBool(1);        public bool IsNull        {            get            {                return _Value == 0;            }        }        public bool IsFalse        {            get            {                return _Value < 0;            }        }        public bool IsTrue        {            get            {                return _Value > 0;            }        }        //bool到DBBool的转换为隐式转换,总不会出错        public static implicit operator TriBool(bool x)        {            return x ? true : false;        }        //反之,必须要清楚的知道自己要做什么        public static explicit operator bool(TriBool x)        {            if(x._Value == 0)            {                throw new InvalidOperationException();            }            return x._Value > 0;        }        //重载 ==        public static TriBool operator ==(TriBool x, TriBool y)        {            if(x._Value == 0 || y._Value == 0)            {                return TriBool.Null;//直接写Null就可以了,我这么写只是为了好理解            }            return x._Value == y._Value ? TriBool.True : TriBool.False;//同上        }                //重载!=        public static TriBool operator !=(TriBool x, TriBool y)        {            if (x._Value == 0 || y._Value == 0)            {                return Null;            }            return !(x == y);//重载!=的惯用手段,当然也可以写成 return x.Value != y.Value ? True : False;        }        //重载!=之前先重载!        public static TriBool operator !(TriBool x)        {            return new TriBool(-x._Value);        }        //重载&        public static TriBool operator &(TriBool x,TriBool y)        {            return new TriBool(x._Value < y._Value ? x._Value : y._Value);//一个为False,返回False,没有False则有一个为Null就返回Null,全为True则返回True        }        //重载|        public static TriBool operator |(TriBool x,TriBool y)        {            return new TriBool(x._Value > y._Value ? x._Value : y._Value);        }        public static bool operator true(TriBool x)        {            return x._Value > 0;        }        public static bool operator false(TriBool x)        {            return x._Value < 0;        }        public override string ToString()        {            if(_Value < 0)            {                return "TriBool.False";            }            if(_Value > 0)            {                return "TriBool.True";            }            return "TriBool.Null";        }        //重写Equals()否则有warning        public override bool Equals(object obj)        {            if(obj == null)            {                return false;            }            if(this.GetType() != obj.GetType())            {                return false;            }            return Equals((TriBool)obj);        }        public bool Equals(TriBool obj)        {            if (this.GetHashCode() != obj.GetHashCode())            {                return false;            }            return this._Value == obj._Value;        }        public override int GetHashCode()        {            int hashCode = base.GetHashCode();            hashCode ^= (_Value + _Value >> 16);            return hashCode;        }      //Main函数      class Program    {        static void Main(string[] args)        {            TriBool[] triBool = { new TriBool(2), new TriBool(0), new TriBool(-1) };           foreach(TriBool item in triBool)            {                if(item)                {                    Console.WriteLine("True value:{0}:{1}", item.ToString(),item.Value);                }                else if(!item)                {                    Console.WriteLine("False value:{0}:{1}", item.ToString(),item.Value);                }                else                {                    Console.WriteLine("Null value:{0}:{1}", item.ToString(),item.Value);                }            }            for(int i = 0;i < triBool.Length;i++)            {                TriBool item1 = triBool[i];                for(int j = i; j < triBool.Length; j++)                {                    TriBool item2 = triBool[j];                    TriBool triBoolOr = item1 | item2;                    TriBool triBoolAnd = item1 & item2;                    TriBool triBoolNot = !item1;                    Console.WriteLine("{0} or {1} is {2}",item1.ToString(),item2.ToString(), triBoolOr ? string.Format("True") : string.Format("False"));                    Console.WriteLine("{0} and {1} is {2}",item1.ToString(),item2.ToString(),triBoolAnd ? string.Format("True") : string.Format("False"));                    Console.WriteLine("Not {0} is {1}", item1.ToString(), triBoolNot ? string.Format("True") : string.Format("False"));                }            }        }    }    /*运行结果    True value:TriBool.True:2    Null value:TriBool.Null:0    False value:TriBool.False:-1    TriBool.True or TriBool.True is True    TriBool.True and TriBool.True is True    Not TriBool.True is False    TriBool.True or TriBool.Null is True    TriBool.True and TriBool.Null is False    Not TriBool.True is False    TriBool.True or TriBool.False is True    TriBool.True and TriBool.False is False    Not TriBool.True is False    TriBool.Null or TriBool.Null is False    TriBool.Null and TriBool.Null is False    Not TriBool.Null is False    TriBool.Null or TriBool.False is False    TriBool.Null and TriBool.False is False    Not TriBool.Null is False    TriBool.False or TriBool.False is False    TriBool.False and TriBool.False is False    Not TriBool.False is True*/

  1. 来自MSDN中文 ↩
  2. 来自C#本质论(第四版) Mark Michaelis, Eric Lippert,周靖译
  3. MSDN中文 ↩
  4. 来自C#本质论(第四版) Mark Michaelis, Eric Lippert,周靖译
0 0
原创粉丝点击