Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Tip
Dieser Artikel ist Teil des Abschnitts "Grundlagen" für Entwickler, die bereits mindestens eine Programmiersprache kennen und C# erlernen. Wenn Sie noch nicht mit der Programmierung vertraut sind, beginnen Sie zuerst mit den Lernprogrammen " Erste Schritte ".
Kommen Sie aus einer anderen Sprache? In Java prüfen == bei Objekten und in JavaScript === bei Objekten die Identität, nicht den Inhalt. C#-Klassen funktionieren standardmäßig auf die gleiche Weise. In Python ruft == standardmäßig __eq__ auf und vergleicht Inhalte, ähnlich wie C#-Records verglichen werden. C# Strukturen werden ebenfalls nach Wert verglichen, wenn Sie Equals aufrufen.
C# unterscheidet zwei Arten von Gleichheit. Wertgleichheit bedeutet, dass zwei Instanzen gleich sind, wenn ihre Daten übereinstimmen. Referenzgleichheit bedeutet, dass zwei Variablen nur gleich sind, wenn sie auf dasselbe Objekt im Arbeitsspeicher verweisen. Diese Bedingung wird auch als Identität bezeichnet. Werttypen vergleichen in der Regel Daten, und Referenztypen vergleichen in der Regel die Identität. Autoren von Typen können diese Voreinstellungen ändern, aber dieses gedankliche Modell verhindert subtile Fehler, bei denen zwei Objekte, die identisch aussehen, nicht als gleich gelten oder bei denen eine Mutation über eine Variable stillschweigend das verändert, was eine andere Variable sieht.
Standardwerte für Werttypen, Referenztypen und Gleichheit
Jeder Typ in C# ist entweder ein Werttyp oder ein Bezugstyp. Ein Werttyp enthält seine Daten direkt in der Variablen. Ein Verweistyp enthält einen Verweis auf ein Objekt. Wenn Sie einer anderen Variablen eine Verweistypvariable zuweisen, verweisen beide Variablen auf dasselbe Objekt. Weitere Informationen zu Werttypen und Referenztypen finden Sie in der Übersicht über das Typsystem.
Das standardmäßige Gleichheitsverhalten richtet sich in der Regel nach der Typart:
-
Integrierte numerische Typen und Enumerationen sind Werttypen. Zwei
intVariablen sind gleich, wenn ihre numerischen Werte übereinstimmen. -
Strukturen sind Werttypen. Eine einfache
structverwendet Wertgleichheit, wenn Sie Equals aufrufen. - Tupel sind Werttypen. Zwei Tupel sind gleich, wenn alle ihre Elementwerte übereinstimmen.
-
Klassen sind Referenztypen. Eine einfache Klasse verwendet die Referenzgleichheit, und
==Equals testen Sie, ob zwei Variablen auf dasselbe Objekt verweisen.
Eine Klasse verwendet Referenzgleichheit. Zwei separate Objekte mit denselben Daten sind nicht gleich, aber zwei Variablen, die auf dasselbe Objekt verweisen, sind gleich:
var order1 = new Order(42, "Shoes");
var order2 = new Order(42, "Shoes");
Console.WriteLine(order1 == order2); // => False
Console.WriteLine(order1.Equals(order2)); // => False
Console.WriteLine(ReferenceEquals(order1, order2)); // => False
Order order3 = order1;
Console.WriteLine(order1 == order3); // => True
Eine struct drückt Wertgleichheit durch Equals aus. Zwei Strukturinstanzen sind gleich, wenn ihre Felder übereinstimmen:
var pt1 = new Point(3, 4);
var pt2 = new Point(3, 4);
Console.WriteLine(pt1.Equals(pt2)); // => True
Einfache Strukturen erhalten keinen vordefinierten == Operator. Das Schreiben p1 == p2 in einer einfachen Struktur kompiliert nur, wenn die Struktur eine eigene operator ==deklariert. Wenn Sie Operatorvergleiche für eine Struktur benötigen, definieren == und != als Paar und halten Sie sie konsistent mit Equals und GetHashCode.
Tupel sind auch Werttypen. Zwei Tupel sind gleich, wenn jeder Elementwert übereinstimmt. Elementnamen in einem benannten Tupel sind lediglich eine Hilfe zur Kompilierzeit und werden bei Vergleichen nicht berücksichtigt. Nur Positionen und Werte sind wichtig:
var t1 = (Name: "Grace", Role: "Engineer");
var t2 = (Name: "Grace", Role: "Engineer");
Console.WriteLine(t1 == t2); // => True
Weitere Informationen zur Tupelsyntax und Dekonstruktion finden Sie unter Tupel und Dekonstruktion.
Verwenden Sie Object.ReferenceEquals, um die Identität direkt zu testen.
ReferenceEquals prüft immer auf Identität, unabhängig davon, wie ein Typ Equals überschreibt oder == überlädt. Verwenden Sie sie als Identitätsdiagnose, wenn Sie bestätigen müssen, ob zwei Variablen auf dasselbe Objekt verweisen:
var doc1 = new Document("Report");
var doc2 = new Document("Report");
var doc3 = doc1;
Console.WriteLine(ReferenceEquals(doc1, doc2)); // => False
Console.WriteLine(ReferenceEquals(doc1, doc3)); // => True
Ein häufiger Anwendungsfall ist, innerhalb einer Equals-Überschreibung den vollständigen Vergleich abzukürzen: Wenn beide Argumente dieselbe Referenz sind, gelten sie immer als gleich, ohne einzelne Felder zu prüfen.
Note
Wenn Variablen als Schnittstelle eingegeben werden, überprüft sie, == ob die Schnittstellenvariablen auf dasselbe Objekt verweisen. Ein Aufruf von Equals führt weiterhin die Implementierung des zugrunde liegenden Objekts aus.
Note
ReferenceEquals gibt immer beim Vergleichen von Werttypen zurück false , auch wenn beide Argumente dieselben Werte enthalten. Dieses Verhalten tritt auf, weil jedes Argument vom Werttyp beim Übergeben an ReferenceEquals unabhängig als separates Heapobjekt geboxt wird.
Typen können unterschiedliche Gleichheitssemantik definieren
Typen können Gleichheitssemantik definieren, die sich vom Standardverhalten unterscheiden. Der häufigste Grund ist die Implementierung der Wertgleichstellung. Wenn Sie einen Typ erstellen, der Daten darstellt, z. B. ein Bankkonto, ein Produkt im Bestand oder ein Benutzer in einem System, sollten Sie Instanzen mit denselben Werten wie gleich betrachten. Wählen Sie Datensatztypen für die Implementierung der Wertgleichstellung aus, und der Compiler generiert alle erforderlichen Gleichheitsmitglieder für Sie.
Note
Zeichenfolgen sind Klassen, aber ==Equals vergleicht Zeichenfolgeninhalt, nicht Identität.
-
==: der Gleichheitsoperator. Die meisten Typen verwenden diesen Operator als Primäre Gleichheitsprüfung. Das Verhalten hängt davon ab, ob der Typ über einen integrierten oder benutzerdefinierten==Operator verfügt. -
!=: der Ungleichheitsoperator. Wenn ein Typ einen benutzerdefinierten==-Operator definiert, muss er auch!=definieren. - Equals: eine virtuelle Methode, die von jedem Typ geerbt wird. Sie können ihn überschreiben, um die Gleichheitssemantik für einen Typ zu ändern.
- GetHashCode: eine virtuelle Methode, die von hashbasierten Sammlungen verwendet wird. Wenn zwei Werte gleich sind, müssen ihre Hashcodes ebenfalls gleich sein.
- ReferenceEquals: eine statische Methode, die immer die Identität testet.
Verwenden von Datensätzen für die Wertgleichstellung
Verwenden Sie den record-Modifier, um einem Typ datenorientierte Wertgleichheit zu verleihen, wenn der Typ ein Datensatz sein kann. Der Compiler generiert Equals, GetHashCodeund ==/!= Member, die jeden deklarierten Eigenschaftswert vergleichen.
A record class ist immer noch ein Verweistyp, vergleicht jedoch Werte anstelle von Identitäten:
var person1 = new Person("Ada", "Lovelace");
var person2 = new Person("Ada", "Lovelace");
Console.WriteLine(person1 == person2); // => True
Console.WriteLine(person1.Equals(person2)); // => True
Console.WriteLine(ReferenceEquals(person1, person2)); // => False
ReferenceEquals bestätigt, dass person1 und person2 unterschiedliche Objekte im Arbeitsspeicher sind, während == und Equals zurückgegeben True werden, da die vom Compiler generierte Gleichheit Eigenschaftswerte vergleicht.
Die gleiche Compilergenerierung gilt für record struct Typen:
var dim1 = new Dimension(1920, 1080);
var dim2 = new Dimension(1920, 1080);
Console.WriteLine(dim1 == dim2); // => True
Console.WriteLine(dim1.Equals(dim2)); // => True
Datensatztypen generieren den gesamten Gleichheitssatz für ihren eigenen Typ. Sowohl die Typen record class als auch record struct überschreiben Equals und GetHashCode. Sie generieren == und != operatoren sowie eine typierte Equals Methode für den Datensatztyp. Im Gegensatz zu einer einfachen struct, record struct unterstützt == und != automatisch. Weitere Informationen zu Datensatztypen und deren Gleichheitssemantik finden Sie unter "Datensätze".
Datensätze mit Mitgliedern vom Referenztyp
Datensatzgleichheit verwendet die eigene Gleichheitssemantik der Mitglieder. Jede Eigenschaft oder jedes Feld wird mithilfe einer eigenen Equals Methode verglichen. Bei den meisten skalaren Werten, z. B. int, string oder DateTime, vergleicht dieser Ansatz die Werte der Datensatzmitglieder. Die Subtilität entsteht bei allgemeinen änderbaren Auflistungen wie List<T> oder T[]: diese Typen vergleichen nach Verweis, sodass zwei Datensatzinstanzen, die unterschiedliche Listenobjekte mit demselben Inhalt enthalten, nicht gleich der synthetisierten Datensatzgleichheit betrachtet werden.
var playlist1 = new Playlist("Chill", new List<string> { "Song A", "Song B" });
var playlist2 = new Playlist("Chill", new List<string> { "Song A", "Song B" });
Console.WriteLine(playlist1.Equals(playlist2)); // => False (different List instances)
Console.WriteLine(playlist1.Tracks.SequenceEqual(playlist2.Tracks)); // => True
playlist1 und playlist2 sind separate List<string> Instanzen. Auch wenn deren Inhalte übereinstimmen, gibt Equalsfalse zurück.
Wenn Sie die Gleichheit von Datensätzen benötigen, um Sammlungsinhalte widerzuspiegeln, haben Sie einige Optionen:
-
Implementieren Sie
IEquatable<T>im Record und überschreiben SieEquals, um Enumerable.SequenceEqual für die Elemente der Sammlung zu verwenden. -
Verwenden Sie einen Sammlungstyp mit Wertgleichheit — zum Beispiel einen benutzerdefinierten
IEqualityComparer<T>oder einen Typ, dessen eigenesEqualsElemente vergleicht. - Identitätsbasiertes Design: wenn der Datensatz eine Entität und keinen reinen Wert darstellt, könnte die Referenzgleichheit seiner Sammlungselemente beabsichtigt sein.
Important
Die manuelle Implementierung der Gleichheit ist heute in C# selten. Datensätze behandeln das allgemeine Szenario der Wertgleichstellung automatisch. Wenn Sie Gleichheit manuell implementieren müssen – zum Beispiel, weil Ihr Typ von einer Basisklasse abgeleitet werden muss, die kein Record ist –, finden Sie in der Sprachreferenz den Abschnitt Implementieren Sie Gleichheit selbst, wenn ein Typ kein Record sein kann.
Siehe auch
- Übersicht über das Typsystem
- Klassen
- Strukturen
- Datensätze
- Tupel und Dekonstruktion.
- Gleichheitsoperatoren (Sprachreferenz)
- Gleichheit in Klassenhierarchien – erweiterte Anleitungen zur polymorphen Gleichheit.
- Arithmetische, Vergleichs-, logische und Zuordnungsoperatoren – die Gleichheitsoperatorenumfrage neben arithmetischen, logischen und Zuordnungsoperatoren.