Sdílet prostřednictvím


Postup: definování rovnosti hodnotu typu (Příručka programování C#)

Při definování třída nebo strukturarozhodnout, zda má smysl vytvořit vlastní definici rovnosti hodnotu (nebo rovnocennost) typu.Obvykle hodnotu rovnosti implementovat očekávaný přidat do kolekce některé řazení objektů typu nebo při jejich hlavním účelem je ukládat sadu polí nebo vlastnosti.Vaše definice rovnosti hodnoty můžete založit na porovnání polí a vlastnosti typu nebo můžete založit definice podmnožina.Ale v obou případech a třídy a struktur, implementace postupujte podle pěti záruky rovnocennosti:

  1. x.Equals(x) vrátí true. se nazývá reflexivní vlastnost.

  2. x.Equals(y) vrátí stejnou hodnotu jako y.Equals(x).To se nazývá symetrické vlastnost.

  3. Pokud (x.Equals(y) & & y.Equals(z)) returns true, then x.Equals(z) returns true.To se nazývá přenositelný vlastnost.

  4. Po sobě jdoucích vyvolání x.Equalsreturn (y), stejná hodnota jako objekty, na které se odkazuje x a y nejsou změněny.

  5. x.Equals(null) returns false.Však null.Equals(NULL) vyvolá výjimka; není to dodržovat pravidlo číslo dvě výše.

Jakékoli struktura , kterou definujete již má implementaci výchozí hodnotu rovnosti, které dědí z System.ValueType přepsat z Object.Equals(Object) metoda. Tato implementace používá reflexe přezkoumat všechny veřejné a neveřejné pole a vlastnosti typu.Ačkoli tato implementace poskytuje správné výsledky, je relativně pomalé srovnání, psát přímo pro typ vlastní implementaci.

Podrobnosti implementace pro hodnotu rovnosti jsou různých tříd a struktur.Třídy a struktur však vyžadují stejnou základní kroky pro provádění rovnosti:

  1. Přepsat virtuálníObject.Equals(Object)metoda. Ve většině případů implementace bool Equals( object obj ) by stejně volání do konkrétní typ Equals metoda je provádění System.IEquatable<T> rozhraní. (Viz krok 2.)

  2. Implementace System.IEquatable<T> rozhraní poskytnutím určitého typu Equals metoda. Je to, kde je provedeno porovnání skutečné rovnocennosti.Například může rozhodnout definovat porovnáním pouze jednu nebo dvě pole v typu rovnost.Není vyvolávají výjimky z Equals.Pouze třídy: Tato metoda by měla zkoumat pouze pole, které jsou deklarované v třída.By měl zavolat base.Equals kontrola polí v základní třída.(Nelze provést v případě typu dědí přímo z Object, protože Object provádění Object.Equals(Object) provede kontrolu rovnosti odkazovat se .)

  3. Nepovinné ale doporučeno: přetěžování == a ! = operátory.

  4. Přepsat Object.GetHashCode tak, aby dva objekty, které mají hodnotu rovnosti vyrábět stejný hodnota hash.

  5. Volitelné: Podpora definice pro "větší než" nebo "menší než" implementovat IComparable<T> rozhraní typu a také přetížení < = a > = operátory.

Prvním příkladu ukazuje implementace třída .Druhý příklad ukazuje struktura provádění.

Příklad

Následující příklad ukazuje, jak implementovat rovnosti hodnota třída (typodkazovat se ).



    namespace ValueEquality
    {
        using System;
        class TwoDPoint : IEquatable<TwoDPoint>
        {
            // Readonly auto-implemented properties.
            public int X { get; private set; }
            public int Y { get; private set; }

            // Set the properties in the constructor.
            public TwoDPoint(int x, int y)
            {
                if ((x < 1) || (x > 2000) || (y < 1) || (y > 2000))
                    throw new System.ArgumentException("Point must be in range 1 - 2000");
                this.X = x;
                this.Y = y;
            }

            public override bool Equals(object obj)
            {
                return this.Equals(obj as TwoDPoint);
            }

            public bool Equals(TwoDPoint p)
            {
                // If parameter is null, return false.
                if (Object.ReferenceEquals(p, null))
                {
                    return false;
                }

                // Optimization for a common success case.
                if (Object.ReferenceEquals(this, p))
                {
                    return true;
                }

                // If run-time types are not exactly the same, return false.
                if (this.GetType() != p.GetType())
                    return false;

                // Return true if the fields match.
                // Note that the base class is not invoked because it is
                // System.Object, which defines Equals as reference equality.
                return (X == p.X) && (Y == p.Y);
            }

            public override int GetHashCode()
            {
                return X * 0x00010000 + Y;
            }

            public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs)
            {
                // Check for null on left side.
                if (Object.ReferenceEquals(lhs, null))
                {
                    if (Object.ReferenceEquals(rhs, null))
                    {
                        // null == null = true.
                        return true;
                    }

                    // Only the left side is null.
                    return false;
                }
                // Equals handles case of null on right side.
                return lhs.Equals(rhs);
            }

            public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs)
            {
                return !(lhs == rhs);
            }
        }

        // For the sake of simplicity, assume a ThreeDPoint IS a TwoDPoint.
        class ThreeDPoint : TwoDPoint, IEquatable<ThreeDPoint>
        {
            public int Z { get; private set; }

            public ThreeDPoint(int x, int y, int z)
                : base(x, y)
            {
                if ((z < 1) || (z > 2000))
                    throw new System.ArgumentException("Point must be in range 1 - 2000");
                this.Z = z;
            }

            public override bool Equals(object obj)
            {
                return this.Equals(obj as ThreeDPoint);
            }

            public bool Equals(ThreeDPoint p)
            {
                // If parameter is null, return false.
                if (Object.ReferenceEquals(p, null))
                {
                    return false;
                }

                // Optimization for a common success case.
                if (Object.ReferenceEquals(this, p))
                {
                    return true;
                }

                // Check properties that this class declares.
                if (Z == p.Z)
                {
                    // Let base class check its own fields 
                    // and do the run-time type comparison.
                    return base.Equals((TwoDPoint)p);
                }
                else
                    return false;
            }

            public override int GetHashCode()
            {
                return (X * 0x100000) + (Y * 0x1000) + Z;
            }

            public static bool operator ==(ThreeDPoint lhs, ThreeDPoint rhs)
            {
                // Check for null.
                if (Object.ReferenceEquals(lhs, null))
                {
                    if (Object.ReferenceEquals(rhs, null))
                    {
                        // null == null = true.
                        return true;
                    }

                    // Only the left side is null.
                    return false;
                }
                // Equals handles the case of null on right side.
                return lhs.Equals(rhs);
            }

            public static bool operator !=(ThreeDPoint lhs, ThreeDPoint rhs)
            {
                return !(lhs == rhs);
            }
        }

        class Program
        {
            static void Main(string[] args)
            {
                ThreeDPoint pointA = new ThreeDPoint(3, 4, 5);
                ThreeDPoint pointB = new ThreeDPoint(3, 4, 5);
                ThreeDPoint pointC = null;
                int i = 5;

                Console.WriteLine("pointA.Equals(pointB) = {0}", pointA.Equals(pointB));
                Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
                Console.WriteLine("null comparison = {0}", pointA.Equals(pointC));
                Console.WriteLine("Compare to some other type = {0}", pointA.Equals(i));

                TwoDPoint pointD = null;
                TwoDPoint pointE = null;



                Console.WriteLine("Two null TwoDPoints are equal: {0}", pointD == pointE);

                pointE = new TwoDPoint(3, 4);
                Console.WriteLine("(pointE == pointA) = {0}", pointE == pointA);
                Console.WriteLine("(pointA == pointE) = {0}", pointA == pointE);
                Console.WriteLine("(pointA != pointE) = {0}", pointA != pointE);

                System.Collections.ArrayList list = new System.Collections.ArrayList();
                list.Add(new ThreeDPoint(3, 4, 5));
                Console.WriteLine("pointE.Equals(list[0]): {0}", pointE.Equals(list[0]));

                // Keep the console window open in debug mode.
                System.Console.WriteLine("Press any key to exit.");
                System.Console.ReadKey();
            }
        }

        /* Output:
            pointA.Equals(pointB) = True
            pointA == pointB = True
            null comparison = False
            Compare to some other type = False
            Two null TwoDPoints are equal: True
            (pointE == pointA) = False
            (pointA == pointE) = False
            (pointA != pointE) = True
            pointE.Equals(list[0]): False
        */
    }

U tříd (odkazovat se typy) výchozí implementace obou Object.Equals(Object) metody provádí porovnání rovnosti odkazovat se , není políčko hodnotu rovnosti.Když rozhodující přepíše virtuální metoda, účelem je jí hodnotu rovnosti sémantika.

== a != operátory lze používat s třídami, i když nemá třída není přetížení je.Výchozí chování je však provést kontrolu rovnosti odkazovat se .třídaPokud přetížení Equals metodaby přetížení == a != operátorů, ale není nutné.

Následující příklad ukazuje, jak implementovat hodnotu rovnosti struktura (hodnota):

    struct TwoDPoint : IEquatable<TwoDPoint>
    {
        // Read/write auto-implemented properties.
        public int X { get; private set; }
        public int Y { get; private set; }

        public TwoDPoint(int x, int y)
            : this()
        {
            X = x;
            Y = x;
        }

        public override bool Equals(object obj)
        {
            if (obj is TwoDPoint)
            {
                return this.Equals((TwoDPoint)obj);
            }
            return false;
        }

        public bool Equals(TwoDPoint p)
        {
            return (X == p.X) && (Y == p.Y);
        }

        public override int GetHashCode()
        {
            return X ^ Y;
        }

        public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs)
        {
            return lhs.Equals(rhs);
        }

        public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs)
        {
            return !(lhs.Equals(rhs));
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            TwoDPoint pointA = new TwoDPoint(3, 4);
            TwoDPoint pointB = new TwoDPoint(3, 4);
            int i = 5;

            // Compare using virtual Equals, static Equals, and == and != operators.
            // True:
            Console.WriteLine("pointA.Equals(pointB) = {0}", pointA.Equals(pointB));
            // True:
            Console.WriteLine("pointA == pointB = {0}", pointA == pointB);
            // True:
            Console.WriteLine("Object.Equals(pointA, pointB) = {0}", Object.Equals(pointA, pointB));
            // False:
            Console.WriteLine("pointA.Equals(null) = {0}", pointA.Equals(null));
            // False:
            Console.WriteLine("(pointA == null) = {0}", pointA == null);
            // True:
            Console.WriteLine("(pointA != null) = {0}", pointA != null);
            // False:
            Console.WriteLine("pointA.Equals(i) = {0}", pointA.Equals(i));
            // CS0019:
            // Console.WriteLine("pointA == i = {0}", pointA == i); 

            // Compare unboxed to boxed.
            System.Collections.ArrayList list = new System.Collections.ArrayList();
            list.Add(new TwoDPoint(3, 4));
            // True:
            Console.WriteLine("pointE.Equals(list[0]): {0}", pointA.Equals(list[0]));


            // Compare nullable to nullable and to non-nullable.
            TwoDPoint? pointC = null;
            TwoDPoint? pointD = null;
            // False:
            Console.WriteLine("pointA == (pointC = null) = {0}", pointA == pointC);
            // True:
            Console.WriteLine("pointC == pointD = {0}", pointC == pointD);

            TwoDPoint temp = new TwoDPoint(3, 4);
            pointC = temp;
            // True:
            Console.WriteLine("pointA == (pointC = 3,4) = {0}", pointA == pointC);

            pointD = temp;
            // True:
            Console.WriteLine("pointD == (pointC = 3,4) = {0}", pointD == pointC);

            // Keep the console window open in debug mode.
            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
    }

    /* Output:
        pointA.Equals(pointB) = True
        pointA == pointB = True
        Object.Equals(pointA, pointB) = True
        pointA.Equals(null) = False
        (pointA == null) = False
        (pointA != null) = True
        pointA.Equals(i) = False
        pointE.Equals(list[0]): True
        pointA == (pointC = null) = False
        pointC == pointD = True
        pointA == (pointC = 3,4) = True
        pointD == (pointC = 3,4) = True
    */
}

Pro struktur výchozí provádění Object.Equals(Object) (což je změněné verze v System.ValueType) provede kontrolu rovnosti hodnotu pomocí reflexe k porovnání hodnot každého pole v typu.Když rozhodující přepíše virtuální Equals metoda stuct účelem je poskytovat efektivní provedení kontroly hodnota rovnosti a volitelně založit porovnání na určitou podmnožinu strukturavlastnosti pole nebo.

== a ! = operátory nemůže pracovat struktura Pokud struktura explicitně přetížení.

Viz také

Koncepty

Příručka programování C#

Další zdroje

Porovnání rovnosti (Příručka programování C#)