Freigeben über


So testen Sie die Referenzgleichheit (Identität) (C#-Programmierhandbuch)

Sie müssen keine benutzerdefinierte Logik implementieren, um Referenzgleichheitsvergleiche in Ihren Typen zu unterstützen. Diese Funktionalität wird für alle Typen durch die statische Object.ReferenceEquals-Methode bereitgestellt.

Im folgenden Beispiel wird gezeigt, wie Sie bestimmen, ob zwei Variablen Referenzgleichheithaben, was bedeutet, dass sie auf dasselbe Objekt im Arbeitsspeicher verweisen.

Das Beispiel zeigt auch, warum Object.ReferenceEquals immer false für Werttypen zurückgibt. Dies ist auf das Boxing zurückzuführen, bei dem für jedes Werttypargument separate Objektinstanzen erstellt werden. Darüber hinaus sollten Sie ReferenceEquals nicht verwenden, um die Zeichenfolgengleichheit zu bestimmen.

Beispiel

using System.Text;

namespace TestReferenceEquality
{
    struct TestStruct
    {
        public int Num { get; private set; }
        public string Name { get; private set; }

        public TestStruct(int i, string s) : this()
        {
            Num = i;
            Name = s;
        }
    }

    class TestClass
    {
        public int Num { get; set; }
        public string? Name { get; set; }
    }

    class Program
    {
        static void Main()
        {
            // Demonstrate reference equality with reference types.
            #region ReferenceTypes

            // Create two reference type instances that have identical values.
            TestClass tcA = new TestClass() { Num = 1, Name = "New TestClass" };
            TestClass tcB = new TestClass() { Num = 1, Name = "New TestClass" };

            Console.WriteLine("ReferenceEquals(tcA, tcB) = {0}",
                                Object.ReferenceEquals(tcA, tcB)); // false

            // After assignment, tcB and tcA refer to the same object.
            // They now have reference equality.
            tcB = tcA;
            Console.WriteLine("After assignment: ReferenceEquals(tcA, tcB) = {0}",
                                Object.ReferenceEquals(tcA, tcB)); // true

            // Changes made to tcA are reflected in tcB. Therefore, objects
            // that have reference equality also have value equality.
            tcA.Num = 42;
            tcA.Name = "TestClass 42";
            Console.WriteLine("tcB.Name = {0} tcB.Num: {1}", tcB.Name, tcB.Num);
            #endregion

            // Demonstrate that two value type instances never have reference equality.
            #region ValueTypes

            TestStruct tsC = new TestStruct( 1, "TestStruct 1");

            // Value types are boxed into separate objects when passed to ReferenceEquals.
            // Even if the same variable is used twice, boxing ensures they are different instances.
            TestStruct tsD = tsC;
            Console.WriteLine("After assignment: ReferenceEquals(tsC, tsD) = {0}",
                                Object.ReferenceEquals(tsC, tsD)); // false
            #endregion

            #region stringRefEquality
            // Constant strings within the same assembly are always interned by the runtime.
            // This means they are stored in the same location in memory. Therefore,
            // the two strings have reference equality although no assignment takes place.
            string strA = "Hello world!";
            string strB = "Hello world!";
            Console.WriteLine("ReferenceEquals(strA, strB) = {0}",
                             Object.ReferenceEquals(strA, strB)); // true

            // After a new string is assigned to strA, strA and strB
            // are no longer interned and no longer have reference equality.
            strA = "Goodbye world!";
            Console.WriteLine("strA = \"{0}\" strB = \"{1}\"", strA, strB);

            Console.WriteLine("After strA changes, ReferenceEquals(strA, strB) = {0}",
                            Object.ReferenceEquals(strA, strB)); // false

            // A string that is created at runtime cannot be interned.
            StringBuilder sb = new StringBuilder("Hello world!");
            string stringC = sb.ToString();
            // False:
            Console.WriteLine("ReferenceEquals(stringC, strB) = {0}",
                            Object.ReferenceEquals(stringC, strB));

            // The string class overloads the == operator to perform an equality comparison.
            Console.WriteLine("stringC == strB = {0}", stringC == strB); // true

            #endregion

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

/* Output:
    ReferenceEquals(tcA, tcB) = False
    After assignment: ReferenceEquals(tcA, tcB) = True
    tcB.Name = TestClass 42 tcB.Num: 42
    After assignment: ReferenceEquals(tsC, tsD) = False
    ReferenceEquals(strA, strB) = True
    strA = "Goodbye world!" strB = "Hello world!"
    After strA changes, ReferenceEquals(strA, strB) = False
    ReferenceEquals(stringC, strB) = False
    stringC == strB = True
*/

Die Implementierung von Equals in der universellen Basisklasse System.Object führt auch eine Überprüfung der Referenzgleichheit aus. Dies ist jedoch am besten nicht zu verwenden, da, wenn eine Klasse die Methode außer Kraft setzt, die Ergebnisse möglicherweise nicht ihren Erwartungen entsprechen. Das gleiche gilt für die Operatoren == und !=. Wenn sie auf Referenztypen ausgeführt werden, besteht das Standardverhalten von == und != darin, eine Referenzgleichheitsprüfung durchzuführen. Abgeleitete Klassen können den Operator jedoch überladen, um eine Überprüfung der Wertgleichstellung durchzuführen. Um das Fehlerpotenzial zu minimieren, empfiehlt es sich, immer ReferenceEquals zu verwenden, wenn Sie bestimmen müssen, ob zwei Objekte eine Referenzgleichheit aufweisen.

Konstantenzeichenfolgen innerhalb der gleichen Assembly werden durch die Laufzeit immer intern gespeichert. Das bedeutet, dass nur eine Instanz jeder eindeutigen Literalzeichenfolge erhalten bleibt. Es gibt jedoch keine Garantie dafür, dass zur Laufzeit erstellte Zeichenfolgen oder zwei gleiche Konstantenzeichenfolgen in unterschiedlichen Assemblys intern gespeichert werden.

Anmerkung

Aufgrund von Boxing gibt ReferenceEquals für Werttypen false zurück, da jedes Argument unabhängig in ein separates Objekt gepackt wird.

Siehe auch