C#之相等比較

C#中的對象都繼承自System.Object對象,分爲引用類型和值類型兩種,所以對象的相等比較而言就分兩種,一種是比較引用,一種是比較值。System.Object默認提供了三個方法來進行對象的相等比較:靜態的ReferenceEquals()和Equals()的兩個版本,加上“==”運算符共有四種來進行對象相等比較的方法。

  相等比較的方法:靜態的ReferenceEquals()、Equals()靜態方法、Equals()虛方法(子類可以去重寫)和“==”運算符。

  相等比較分類:引用類型比較(類的實例)、值類型比較(基本數據類型,結構或者枚舉的實例)。

  但對於引用類型和值類型而言,同一個方法它們的內部比較邏輯是不一樣的,下面進行下簡單的介紹。

一、引用類型相等比較

1、靜態的ReferenceEquals()

  ReferenceEquals()是一個靜態方法,比較兩個對象是否引用自同一個地址,是則返回true,否則返回false

  調用方法:ReferenceEquals(obj1,obj2)

  比較原則:1)、obj1和obj2同爲null,則返回true

         2)、obj1和obj2只有一個爲null,則返回false

       3)、obj1和obj2均不爲null時,比較兩個對象的引用地址,是則返回true,不是則返回false

  例子:

    SomeClass x,y;

    x = new SomeClass();

    y = new SomeClass();

    z = y;

    Boolean result1 = ReferenceEquals(null,null);  //return true

    Boolean result2 = ReferenceEquals(null,x);   //return false

    Boolean result3 = ReferenceEquals(x,y);    //return false

    Boolean result4 = ReferenceEquals(y,z);    //return true

2、虛擬的Equals()方法

  System.Object()的虛擬的Equals()方法也是比較引用的,但是因爲它是虛擬的,所以繼承的子類可以重寫該方法以實現按值來比較對象,在重寫Equals()方法時最好重寫對象的GetHashCode()方法.

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Point p1 = new Point(5, 2);
            Point p2 = new Point(5, 2);
            Point3D p3 = new Point3D(5, 2, 1);
            Point3D p4 = new Point3D(5, 2, 1);

            if (p1.Equals(p2))
                Console.WriteLine("p1 is equals p2");
            if (p3.Equals(p4))
                Console.WriteLine("p3 is equals p4");

            if (!p1.Equals(p3))
                Console.WriteLine("p1 is not equals p3");

            if (!p3.Equals(p1))
                Console.WriteLine("p3 is not equals p1");
        }
    }

    public class Point
    {
        private Int32 x;
        private Int32 y;

        public Point()
        {
            this.x = 0;
            this.y = 0;
        }
        public Point(Int32 _x, Int32 _y)
        {
            this.x = _x;
            this.y = _y;
        }

        public Int32 X
        {
            get { return x; }
            set { x = value; }
        }

        public Int32 Y
        {
            get { return y; }
            set { y = value; }
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            if (this.GetType() != obj.GetType())
                return false;


            return Equals((Point)obj);
        }

        public override int GetHashCode()
        {
            return x ^ y;
        }

        public override string ToString()
        {
            return String.Format("X:{0},Y:{1}", this.x, this.y);
        }

        private bool Equals(Point p)
        {
            return (this.x == p.x) && (this.y == p.y);
        }
    }

    public class Point3D : Point
    {
        private Int32 z;
        public Int32 Z
        {
            get { return this.z; }
            set { z = value; }
        }

        public Point3D()
            : base()
        {
            this.z = 0;
        }

        public Point3D(Int32 _x, Int32 _y, Int32 _z)
            : base(_x, _y)
        {
            this.z = _z;
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            if (GetType() != obj.GetType())
                return false;

            Point3D p3d = obj as Point3D;

            if (p3d.z != this.z)
                return false;

            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode() ^ z;
        }

        public override string ToString()
        {
            return String.Format("X:{0},Y:{1},Z:{2}", base.X, base.Y, Z);
        }
    }
}

3、靜態的Equals()方法

  Eauals()靜態方法的比較原則是按照引用的方式比較,再調用對象的Equals()方法的實例版本進行比較,所以在重寫對象的Equals()方法時,其實已經間接的重寫了靜態的Equals()方法。

  調用方法:Equals(obj1,obj2)

  比較原則:1)、obj1和obj2均爲null,則返回true

       2)、obj1和obj2中只有一個爲null,則返回false

       3)、如果obj1和obj2兩個引用不指向同一個對象,則返回false

       4)、如果obj1和obj2兩個引用指向同一個對象,則調用它們的Equals()方法的實例版本進行比較

4、“==”比較運算符

  在默認情況下,==運算符對引用類型比較的是兩個對象指向的引用是否是同一個對象,但是作爲一個自定義的複雜類,可以自己重寫適合自己的“==”運算符,在重寫“==”時必須同時重寫“!=”運算符。

  例1:在沒有重寫“==”時,我們看下兩個類的“==”的比較結果(比較是否指向同一個引用)

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Point p1 = new Point(5, 2);
            Point p2 = new Point(5, 2);
            Point p21 = p1;

            if (p1.Equals(p2))
                Console.WriteLine("p1 is equals p2");

            if (!(p1 == p2))
                Console.WriteLine("p1 is not == p2");

            if (p1 == p21)
                Console.WriteLine("p1 is == p21");
        }
    }

    public class Point
    {
        private Int32 x;
        private Int32 y;

        public Point()
        {
            this.x = 0;
            this.y = 0;
        }
        public Point(Int32 _x, Int32 _y)
        {
            this.x = _x;
            this.y = _y;
        }

        public Int32 X
        {
            get { return x; }
            set { x = value; }
        }

        public Int32 Y
        {
            get { return y; }
            set { y = value; }
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            if (this.GetType() != obj.GetType())
                return false;


            return Equals((Point)obj);
        }

        public override int GetHashCode()
        {
            return x ^ y;
        }

        public override string ToString()
        {
            return String.Format("X:{0},Y:{1}", this.x, this.y);
        }

        private bool Equals(Point p)
        {
            return (this.x == p.x) && (this.y == p.y);
        }
    }
}

  運算結果:

  例2:我們重寫“==”運算符(比較兩個對象對應值是否相等),這時再看下結果

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Point p1 = new Point(5, 2);
            Point p2 = new Point(5, 2);
            Point p21 = p1;

            if (p1.Equals(p2))
                Console.WriteLine("p1 is equals p2");

            if (p1 == p2)
                Console.WriteLine("p1 is == p2");

            if (p1 == p21)
                Console.WriteLine("p1 is == p21");
        }
    }

    public class Point
    {
        private Int32 x;
        private Int32 y;

        public Point()
        {
            this.x = 0;
            this.y = 0;
        }
        public Point(Int32 _x, Int32 _y)
        {
            this.x = _x;
            this.y = _y;
        }

        public Int32 X
        {
            get { return x; }
            set { x = value; }
        }

        public Int32 Y
        {
            get { return y; }
            set { y = value; }
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;

            if (this.GetType() != obj.GetType())
                return false;


            return Equals((Point)obj);
        }

        /// <summary>
        /// 重寫相等運算符
        /// </summary>
        public static Boolean operator ==(Point p1, Point p2)
        {
            return (p1.x == p2.x) && (p1.y == p2.y);
        }

        /// <summary>
        /// 重寫不相等運算符
        /// </summary>
        public static Boolean operator !=(Point p1, Point p2)
        {
            return !(p1.x == p2.x) && (p1.y == p2.y);
        }

        public override int GetHashCode()
        {
            return x ^ y;
        }

        public override string ToString()
        {
            return String.Format("X:{0},Y:{1}", this.x, this.y);
        }

        private bool Equals(Point p)
        {
            return (this.x == p.x) && (this.y == p.y);
        }
    }
}

  運算結果:

 

二、值類型相等比較

1、靜態的ReferenceEquals()

  ReferenceEquals()方法用於比較引用,在比較之前,C#會先通過裝箱技術對每個值類型參數進行分別裝箱,這樣ReferenceEquals()方法進行比較時得到的結果永遠時false,所以用ReferenceEquals()來比較值類型是沒有什麼意義的。

2、虛擬的Equals()方法、靜態的Equals()方法和“==”運算符

  對於值類型,這三個方法默認都是進行值比較的。

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {

            Int32 a = 5;
            Int32 b = 5;

            if (a.Equals(b))
                Console.WriteLine("a is equals b");
            if (a == b)
                Console.WriteLine("a is == b");

            if (Equals(a, b))
                Console.WriteLine("a is equals b");

            if (!ReferenceEquals(a, b))
                Console.WriteLine("a is not ReferenceEquals b");
        }
    }
}

發佈了55 篇原創文章 · 獲贊 21 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章