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.
Die Operatoren == (Gleichheit) und != (Ungleichheit) überprüfen, ob die Operanden gleich sind oder nicht. Werttypen sind gleich, wenn ihr Inhalt gleich ist. Verweistypen sind gleich, wenn die beiden Variablen auf denselben Speicher verweisen.
Die C#-Sprachreferenz dokumentiert die zuletzt veröffentlichte Version der C#-Sprache. Außerdem enthält sie eine erste Dokumentation zu Funktionen in der öffentlichen Vorschau für die kommende Sprachversion.
In der Dokumentation werden alle Features identifiziert, die in den letzten drei Versionen der Sprache oder in der aktuellen öffentlichen Vorschau eingeführt wurden.
Tipp
Informationen dazu, wann ein Feature erstmals in C# eingeführt wurde, finden Sie im Artikel zum Versionsverlauf der C#-Sprache.
Sie können den is Musterabgleichsoperator als Alternative zu einem == Test verwenden, wenn Sie einen Konstantenwert testen. Der is Operator verwendet die Standardgleichheitssemantik für alle Wert- und Referenztypen.
Gleichheitsoperator ==
Der Gleichheitsoperator == gibt true zurück, wenn die Operanden gleich sind; andernfalls false.
Gleichheit von Werttypen
Operanden der integrierten Werttypen sind gleich, wenn ihre Werte gleich sind:
int a = 1 + 2 + 3;
int b = 6;
Console.WriteLine(a == b); // output: True
char c1 = 'a';
char c2 = 'A';
Console.WriteLine(c1 == c2); // output: False
Console.WriteLine(c1 == char.ToLower(c2)); // output: True
Hinweis
Für die ==Operatoren ,< , >und , <=>= wenn einer der Operanden keine Zahl (Double.NaN oder Single.NaN) ist, ist falsedas Ergebnis des Vorgangs . Diese Bedingung bedeutet, dass der NaN Wert nicht größer als, kleiner oder gleich einem anderen double (oder float) Wert ist, einschließlich NaN. Weitere Informationen und Beispiele finden Sie im Double.NaN- oder Single.NaN-Referenzartikel.
Zwei Operanden desselben enum-Typs sind gleich, wenn die entsprechenden Werte des zugrunde liegenden integralen Typs gleich sind.
Benutzerdefinierte Strukturtypen unterstützen den ==-Operator nicht standardmäßig. Eine benutzerdefinierte Struktur muss den ==-Operator überladen, damit er unterstützt wird.
C#- Tupel verfügen über integrierte Unterstützung für die == Und-Operatoren != . Weitere Informationen finden Sie im Abschnitt Tupelgleichheit im Artikel Tupeltypen.
Gleichheit von Verweistypen
Standardmäßig sind Verweistypopernden, mit Ausnahme von Datensätzen, gleich, wenn sie auf dasselbe Objekt verweisen:
public class ReferenceTypesEquality
{
public class MyClass
{
private int id;
public MyClass(int id) => this.id = id;
}
public static void Main()
{
var a = new MyClass(1);
var b = new MyClass(1);
var c = a;
Console.WriteLine(a == b); // output: False
Console.WriteLine(a == c); // output: True
}
}
Wie im vorherigen Beispiel gezeigt, unterstützen benutzerdefinierte Verweistypen standardmäßig den == Operator. Ein Verweistyp kann den Operator == aber überladen. Wenn ein Verweistyp den ==-Operator überlädt, verwenden Sie die Object.ReferenceEquals-Methode, um zu überprüfen, ob zwei Verweise dieses Typs auf dasselbe Objekt verweisen.
Gleichheit von Datensatztypen
Datensatztypen unterstützen die Operatoren == und !=, die standardmäßig Semantik für Wertgleichheit ausdrücken. Das heißt, zwei Datensatzopernden sind gleich, wenn beide werte oder entsprechende Werte aller Felder sind null und automatisch implementierte Eigenschaften gleich sind.
public class RecordTypesEquality
{
public record Point(int X, int Y, string Name);
public record TaggedNumber(int Number, List<string> Tags);
public static void Main()
{
var p1 = new Point(2, 3, "A");
var p2 = new Point(1, 3, "B");
var p3 = new Point(2, 3, "A");
Console.WriteLine(p1 == p2); // output: False
Console.WriteLine(p1 == p3); // output: True
var n1 = new TaggedNumber(2, new List<string>() { "A" });
var n2 = new TaggedNumber(2, new List<string>() { "A" });
Console.WriteLine(n1 == n2); // output: False
}
}
Wie im vorstehenden Beispiel gezeigt, wird die Gleichheit von Referenztypmitgliedern mit ihren spezifischen Gleichheitsimplementierungen verglichen.
Zeichenfolgengleichheit
Zwei Zeichenfolge-Operanden gleich sind, wenn beide gleich null sind oder wenn beide Zeichenfolgeninstanzen dieselbe Länge und identische Zeichen in jeder Zeichenposition haben:
string s1 = "hello!";
string s2 = "HeLLo!";
Console.WriteLine(s1 == s2.ToLower()); // output: True
string s3 = "Hello!";
Console.WriteLine(s1 == s3); // output: False
Bei Vergleichen der Gleichheit von Zeichenfolgen wird Groß-/Kleinschreibung beachtet. Weitere Informationen zum Zeichenfolgenvergleich finden Sie unter Vergleichen von Zeichenfolgen in C#.
Delegatengleichheit
Zwei Stellvertretungsopernden des gleichen Laufzeittyps sind gleich, wenn beide oder deren Aufruflisten dieselbe Länge haben null und in jeder Position gleiche Einträge haben:
Action a = () => Console.WriteLine("a");
Action b = a + a;
Action c = a + a;
Console.WriteLine(object.ReferenceEquals(b, c)); // output: False
Console.WriteLine(b == c); // output: True
Von Bedeutung
Gleiche Einträge in einer Aufrufliste enthalten alle festen Parameter im Aufruf, einschließlich des Empfängers. Der Empfänger ist die Instanz eines Objekts, das dargestellt this wird, wenn der Eintrag aufgerufen wird.
var o1 = new object();
var o2 = new object();
var d1 = o1.ToString;
var d2 = o2.ToString;
Console.WriteLine(object.ReferenceEquals(d1, d2)); // output: False
Console.WriteLine(d1 == d2); // output: False (different receivers)
Weitere Informationen finden Sie im Abschnitt Operatoren für Delegatengleichheit der C#-Sprachspezifikation.
Stellvertretungen, die aus der Auswertung semantisch identischer Lambda-Ausdrücke stammen, sind nicht gleich, wie das folgende Beispiel zeigt:
Action a = () => Console.WriteLine("a");
Action b = () => Console.WriteLine("a");
Console.WriteLine(a == b); // output: False
Console.WriteLine(a + b == a + b); // output: True
Console.WriteLine(b + a == a + b); // output: False
Ungleichheitsoperator !=
Der Ungleichheitsoperator != gibt zurück true , wenn die Operanden nicht gleich sind und false andernfalls. Für die Operanden der integrierten Typen führt der Ausdruck x != y zum selben Ergebnis wie der Ausdruck !(x == y). Weitere Informationen zur Typengleichheit finden Sie im Abschnitt Gleichheitsoperator.
Im folgenden Beispiel wird die Verwendung des != Operators veranschaulicht:
int a = 1 + 1 + 2 + 3;
int b = 6;
Console.WriteLine(a != b); // output: True
string s1 = "Hello";
string s2 = "Hello";
Console.WriteLine(s1 != s2); // output: False
object o1 = 1;
object o2 = 1;
Console.WriteLine(o1 != o2); // output: True
Gleichheit in Klassenhierarchien
Datensätze behandeln die Vererbung ordnungsgemäß ohne manuelle Arbeit. Die vom Compiler generierte Gleichheit überprüft sowohl den Laufzeittyp als auch alle deklarierten Eigenschaften, sodass sie automatisch die Symmetrie- und Transitivitätsanforderungen erfüllt. Bevorzugen Sie record es vor einer manuellen unsealierten Hierarchie, wenn die Wertgleichstellung das Ziel ist.
Von Bedeutung
Verwenden Sie record nach Möglichkeit – der Compiler generiert alle erforderlichen Gleichheitselemente für Sie. Manuelle Implementierung ist nur erforderlich, wenn Ihr Typ von einer Nicht-Datensatzklasse abgeleitet werden muss oder andere Einschränkungen aufweist, die verhindert recordwerden.
Gleichheit selbst implementieren, wenn ein Typ kein Record sein kann
Hier ist eine minimale manuelle Implementierung für einen Werttyp, der kein Datensatz sein kann:
class Color : IEquatable<Color>
{
public Color(int r, int g, int b)
{
R = r;
G = g;
B = b;
}
public int R { get; }
public int G { get; }
public int B { get; }
public bool Equals(Color? other) =>
other is not null && R == other.R && G == other.G && B == other.B;
public override bool Equals(object? obj) => obj is Color other && Equals(other);
public override int GetHashCode() => HashCode.Combine(R, G, B);
}
Die Implementierung stellt drei erforderliche Elemente bereit: Equals(T?) als Kernvergleich für override Equals(object?) Aufrufe auf Objektebene und override GetHashCode() damit hashbasierte Auflistungen ordnungsgemäß funktionieren.
HashCode.Combine ist ein Bibliothekshilfsprogramm, das einen Hash aus denselben Werten erstellt, die von Equals. Die Implementierung IEquatable<T> (die Equals(T?) Überladung) ist optional, verhindert jedoch das Boxen, wenn Anrufer bereits über den konkreten Typ verfügen.
Wenn Sie diese auch als Paar definieren und !=festlegen==, erfordert die Sprache diese. Warnungen CS0660 und CS0661 erinnern Sie daran, alle vier Member konsistent zu halten.
Wenn die oben genannten Equals drei Mitglieder die Wertgleichheit widerspiegeln, die Identität aber == dennoch überprüft werden, weil noch kein == Operator deklariert wurde:
var red1 = new Color(255, 0, 0);
var red2 = new Color(255, 0, 0);
Console.WriteLine(red1.Equals(red2)); // => True
Console.WriteLine(red1 == red2); // => False (no == overload; identity check)
Eine korrekte Implementierung muss auch den Äquivalenzvertrag erfüllen (vorausgesetzt x, yund z sind ungleich NULL):
-
Reflexiv:
x.Equals(x)gibt zurücktrue. -
Symmetrisch:
x.Equals(y)gibt denselben Wert wiey.Equals(x). -
Transitiv: wenn
x.Equals(y)undy.Equals(z)beides sindtrue, muss esx.Equals(z)sich umtrue. -
Konsistent: Aufeinander folgende Aufrufe, um
x.Equals(y)denselben Wert zurückzugeben, solange keines der Objekte geändert wird. -
Null-Verhalten:
x.Equals(null)Gibt zurückfalse;x.Equals(y)darf nicht ausgelöst werden, wenn für einen Nicht-Null-Wertxaufgerufen wird.
Die Wertgleichheit in einer unsealierten Klassenhierarchie erfordert mehr Sorgfalt als in einer versiegelten Klasse, um die symmetrischen und transitiven Regeln zu erfüllen. Die Gefahr besteht darin, dass IEquatable<T>.Equals(T? other) die Versendung den deklarierten Typ (der in der Variablendeklaration geschriebene Typ) der Variablen und nicht dem Laufzeittyp folgt. Wenn Shape eine Nicht-DeklarierungvirtualEquals(Shape? other) erfolgt, wird eine Variable, die so eingegeben wird, wie Shape sie zur Circle Laufzeit aufgerufen wird Shape.Equals, im Hintergrund Circle-spezifische Felder ignoriert. Zwei Circle Objekte mit unterschiedlichen Bogenmaßen können beim Zugriff über eine Shape Variable gleich verglichen werden.
Das richtige Muster erfordert zwei kooperierende Anforderungen: Stellen Sie die typierte Methode virtual so, dass jede abgeleitete Equals Klasse den Vergleich erweitern kann, und fügen Sie in der Basisklassenimplementierung einen GetType() == other.GetType() Guard hinzu, sodass Objekte unterschiedlicher Laufzeittypen niemals gleich angesehen werden.
Basisklassenimplementierung
// Shape is an unsealed base class. Making Equals virtual and guarding with GetType()
// ensures a derived instance is never equal to an instance of a different runtime type.
class Shape : IEquatable<Shape>
{
public string Color { get; }
public Shape(string color) => Color = color;
public override bool Equals(object? obj) => Equals(obj as Shape);
// virtual so derived classes can override and augment the comparison
public virtual bool Equals(Shape? other) =>
other is not null &&
GetType() == other.GetType() && // reject different runtime types
Color == other.Color;
// GetType() is included because equality requires matching runtime types
public override int GetHashCode() => HashCode.Combine(GetType(), Color);
public static bool operator ==(Shape? l, Shape? r) => l?.Equals(r) ?? r is null;
public static bool operator !=(Shape? l, Shape? r) => !(l == r);
}
Wichtige Punkte:
-
virtualtypedEquals: Jede abgeleitete Klasse setzt diese Methode außer Kraft, um den Vergleich mit eigenen Feldern zu erweitern. -
GetType()guard:GetType() == other.GetType()verhindert, dass einCircleShapeGleichheitszeichen mit derselben Farbe besteht, und verhindert, dass Objekte unterschiedlicher abgeleiteter Typen einander entsprechen. -
GetHashCodeincludesGetType(): Because two objects are equal only when their runtime types match,GetHashCodemust hash the runtime type as well the data fields. Das AuslassenGetType()hier führt zu falschem Verhalten inDictionary<TKey,TValue>undHashSet<T>. -
==Stellvertretungen anEquals: hält die Gleichheit von Operatoren und Methoden konsistent.
Abgeleitete Klassenimplementierung
Eine abgeleitete Klasse, die Felder hinzufügt, überschreibt den typierten EqualsTyp, wandelt in einen eigenen Typ um, ruft auf base.Equalsund vergleicht dann seine eigenen Felder:
class Circle : Shape
{
public double Radius { get; }
public Circle(string color, double radius) : base(color) => Radius = radius;
public override bool Equals(object? obj) => Equals(obj as Shape);
// Calls base.Equals to verify Color and runtime type, then adds Radius
public override bool Equals(Shape? other) =>
other is Circle c && base.Equals(c) && Radius == c.Radius;
public override int GetHashCode() => HashCode.Combine(GetType(), Color, Radius);
}
base.Equals(c) erzwingt den GetType() Schutz und überprüft die freigegebenen Felder. Der Umwandlungsweg other is Circle c schlägt schnell fehl, wenn das Argument ein Shape anderer abgeleiteter Typ ist.
Verwendung über eine Basistypvariable
Shape circle1 = new Circle("red", 5.0);
Shape circle2 = new Circle("red", 7.0);
Shape circle3 = new Circle("red", 5.0);
Shape shape1 = new Shape("red");
Console.WriteLine(circle1.Equals(circle2)); // => False (Radius differs)
Console.WriteLine(circle1.Equals(circle3)); // => True
Console.WriteLine(circle1.Equals(shape1)); // => False (different runtime types)
Versiegelte Klassen sind einfacher
Sie können eine sealed Klasse nicht unterklassen, sodass kompilierte Zeit- und Laufzeittypen immer zustimmen. Sie benötigen nicht die Wache oder virtual den GetType() Versand. Das IEquatable<T> Muster in " Gleichheit selbst implementieren", wenn ein Typ kein Datensatz sein kann , ist korrekt und vollständig für eine versiegelte Klasse.
Operatorüberladbarkeit
Sie können die Operatoren und == Operatoren in einem benutzerdefinierten Typ !=. Wenn Sie einen dieser beiden Operatoren überladen, müssen Sie auch den anderen Operator überladen.
Sie können die == Operatoren und != Operatoren in einem Datensatztyp nicht explizit überladen. Um das Verhalten der == Datensätze != und Operatoren für den Datensatztyp Tzu ändern, implementieren Sie die IEquatable<T>.Equals Methode mit der folgenden Signatur:
public virtual bool Equals(T? other);
C#-Sprachspezifikation
Weitere Informationen finden Sie im Abschnitt Relationale und Typtestoperatoren in der C#-Sprachspezifikation.
Weitere Informationen zur Gleichheit von Datensatztypen finden Sie im Abschnitt " Gleichheitsmitglieder " der C#-Sprachspezifikation.