zoukankan      html  css  js  c++  java
  • 如何使用==操作符,Equals方法,ReferenceEquals方法,IEquatable接口比较2个对象

    “世界上不会有两片完全相同的树叶”,这句话适用于现实世界。而在软件世界中,这句话变成了"世界上必须有两片完全相同的树叶",否则,很多事情无以为继。

     

    当比较2个对象是否相等时,通常情况下:==操作符用来比较值类型,比较的是值;实例方法Equals和静态方法Object.ReferenceEquals比较引用类型,比较的是对象的地址。

     

    在实际项目中,当比较2个引用类型对象时,我们的需求变为:通过依次比较2个对象的所有属性来判断是否相等。这时候,IEquatable接口就有了展示自己的机会。本篇主要包括:

     

      使用==操作符比较值类型是否相等

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                ComplexNumber a = new ComplexNumber(){Real = 4.5D, Imaginary = 8.4D};
    
                ComplexNumber b = new ComplexNumber() { Real = 4.5D, Imaginary = 8.4D };
    
                Console.WriteLine("{0} 是否等于{1}:{2}",a, b, CompareTwoComplexNumbers(a, b));
    
                Console.ReadKey();
    
            }
    
            static bool CompareTwoComplexNumbers(ComplexNumber a, ComplexNumber b)
    
            {
    
                return ((a.Real == b.Real) && (a.Imaginary == b.Imaginary));
    
            }
    
        }
    
        public class ComplexNumber
    
        {
    
            public double Real { get; set; }
    
            public double Imaginary { get; set; }
    
            public override string ToString()
    
            {
    
                return String.Format("{0}{1}{2}i",
    
                    Real,
    
                    Imaginary >= 0 ? "+" : "-",
    
                    Math.Abs(Imaginary));
    
            }
    
        }
    

    1


    以上,比较诸如int,double,DateTime,struct等值类型的时候,==操作符当然是不二之选。

      实例方法Equals比较引用类型地址是否相等

        class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Guy darren1 = new Guy("Darren", 37, 100);
    
                Guy darren2 = darren1;
    
                Console.WriteLine(Object.ReferenceEquals(darren1,darren2));
    
                Console.WriteLine(darren1.Equals(darren2));
    
                Console.WriteLine(Object.ReferenceEquals(null, null));
    
                darren2 = new Guy("Darren", 37, 100);
    
                Console.WriteLine(Object.ReferenceEquals(darren1, darren2));
    
                Console.WriteLine(darren1.Equals(darren2));
    
                Console.ReadKey();
    
            }
    
        }
    
        public class Guy
    
        {
    
            private readonly string name;
    
            public string Name{get { return name; }}
    
            private readonly int age;
    
            public int Age{get { return age; }}
    
            public int Cash { get; private set; }
    
            public Guy(string name, int age, int cash)
    
            {
    
                this.name = name;
    
                this.age = age;
    
                Cash = cash;
    
            }
    
            public override string ToString()
    
            {
    
                return String.Format("{0} 今年 {1} 岁了,身价{2}", Name, Age, Cash);
    
            }
    
        }
    

    2

    以上,实例方法Equals()和静态方法Object.ReferenceEquals()适用于比较引用类型,而且比较的是对象的地址。


    可是,如果我们想使用Equals()方法比较引用类型对象的各个属性,怎么办呢?

      实现IEquatable接口重写实例方法Equals()

    写一个派生于Guy的类EquatableGuy,并且实现IEquatable<Guy>接口,重写IEquatable<Guy>接口的Equals(Guy other)方法。

       class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                Guy darren1 = new EquatableGuy("Darren", 37, 100);
    
                Guy darren2 = new EquatableGuy("Darren", 37, 100);
    
                Console.WriteLine(Object.ReferenceEquals(darren1, darren2)); //False
    
                Console.WriteLine(darren1.Equals(darren2)); //True
    
                Console.ReadKey();
    
            }
    
        }
    
        public class Guy
    
        {
    
            private readonly string name;
    
            public string Name{get { return name; }}
    
            private readonly int age;
    
            public int Age{get { return age; }}
    
            public int Cash { get; private set; }
    
            public Guy(string name, int age, int cash)
    
            {
    
                this.name = name;
    
                this.age = age;
    
                Cash = cash;
    
            }
    
            public override string ToString()
    
            {
    
                return String.Format("{0} 今年 {1} 岁了,身价{2}", Name, Age, Cash);
    
            }
    
        }
    
        public class EquatableGuy : Guy, IEquatable<Guy>
    
        {
    
            public EquatableGuy(string name, int age, int cash) : base(name, age, cash){}
    
            public bool Equals(Guy other)
    
            {
    
                if (ReferenceEquals(null, other)) return false;
    
                if (ReferenceEquals(this, other)) return true;
    
                return Equals(other.Name, Name) && other.Age == Age && other.Cash == Cash;
    
            }
    
            public override bool Equals(object obj)
    
            {
    
                if (!(obj is Guy)) return false;
    
                return Equals((Guy) obj);
    
            }
    
            public override int GetHashCode()
    
            {
    
                const int prime = 397;
    
                int result = Age;
    
                result = (result * prime) ^ (Name != null ? Name.GetHashCode() : 0);
    
                result = (result * prime) ^ Cash;
    
                return result;
    
            }
    
        }
    

    3

    以上,值得注意的是:
    当实现IEquatable<Guy>接口时,一定要重写Object基类的Equals方法,然后在Object基类的Equals方法内部调用我们自定义的IEquatable<Guy>接口方法。另外还必须重写Object基类的GetHashCode方法。这时MSDN规定的,
    在这里

     

    而且,使用IEquatable<Guy>泛型接口还有一个好处是避免装箱和拆箱,因为在JIT编译时才替代占位符。而如果我们通过重写Object基类方法Equals实现自定义比较的话,难免会出现装箱和拆箱,影响比较性能。


    如果我们项使用==操作符比较引用对象是否相等呢?我们可以通过重写操作符来实现。

     

    我们再写一个EquatableGuy的子类EquatableGuyWithOverload,并且重写==操作符。

       class Program
    
        {
    
            static void Main(string[] args)
    
            {
    
                var darren1 = new EquatableGuyWithOverload("Darren", 37, 100);
    
                var darren2 = new EquatableGuyWithOverload("Darren", 37, 100);
    
                Console.WriteLine(Object.ReferenceEquals(darren1, darren2)); //False
    
                Console.WriteLine(darren1 == darren2); //True
    
                Console.ReadKey();
    
            }
    
        }
    
        public class EquatableGuyWithOverload : EquatableGuy
    
        {
    
            public EquatableGuyWithOverload(string name, int age, int cash) : base(name, age, cash){}
    
            public static bool operator ==(EquatableGuyWithOverload left, EquatableGuyWithOverload right)
    
            {
    
                if (Object.ReferenceEquals(left, null)) return false;
    
                else return left.Equals(right);
    
            }
    
            public static bool operator !=(EquatableGuyWithOverload left, EquatableGuyWithOverload right)
    
            {
    
                return !(left == right);
    
            }
    
            public override bool Equals(object obj)
    
            {
    
                return base.Equals(obj);
    
            }
    
            public override int GetHashCode()
    
            {
    
                return base.GetHashCode();
    
            }
    
        }
    

    3

    以上,子类EquatableGuyWithOverload重写了==操作符,由于其父类EquatableGuy已经重写了基类Object的Equals方法,所以在这里可以直接调用。

    总结:通常情况下,使用==操作符比较值类型对象;使用实例方法Equals或静态方法Object.ReferenceEquals比较引用类型对象地址;如果想自定义比较逻辑,可以考虑实现IEquatable<>泛型接口,避免装箱、拆箱。

    参考资料:

    防止装箱落实到底,只做一半也是失败
    Understanding C#: Equality, IEquatable, and Equals()
  • 相关阅读:
    博客园设置简约主题
    day25-48
    day1-10
    drf app
    flask 框架 练习
    vue入门 显示数据 操作属性 操作样式 条件渲染
    flask 配置文件 路由 视图 模板 中间件
    flask 使用数据库连接池
    Android 工程中添加依赖
    apk反编译
  • 原文地址:https://www.cnblogs.com/darrenji/p/3920171.html
Copyright © 2011-2022 走看看