Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of mappen te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen om mappen te wijzigen.
De == operators (gelijkheid) en!= controleren of hun operanden gelijk zijn of niet. Waardetypen zijn gelijk wanneer de inhoud gelijk is. Verwijzingstypen zijn gelijk wanneer de twee variabelen naar dezelfde opslag verwijzen.
De C#-taalreferentiedocumenten beschrijven de meest recent uitgebrachte versie van de C#-taal. Het bevat ook de eerste documentatie voor functies in openbare previews voor de aanstaande taalrelease.
De documentatie identificeert alle functies die voor het eerst zijn geïntroduceerd in de laatste drie versies van de taal of in de huidige openbare previews.
Aanbeveling
Raadpleeg het artikel over de versiegeschiedenis van de C#-taal om te achterhalen wanneer een functie voor het eerst is geïntroduceerd in C#.
U kunt de is patroonkoppelingsoperator gebruiken als alternatief voor een == test wanneer u test op basis van een constante waarde. De is operator maakt gebruik van de standaard gelijkheidssemantiek voor alle waarde- en verwijzingstypen.
Gelijkheidsoperator ==
De gelijkheidsoperator retourneert ==true als de operanden gelijk zijn, false anders.
Gelijkheid van waardetypen
Operanden van de ingebouwde waardetypen zijn gelijk als de waarden gelijk zijn:
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
Notitie
Voor de ==operatoren ,< , >en <=>= operators, als een van de operanden geen getal (Double.NaN of Single.NaN), het resultaat van de bewerking isfalse. Deze voorwaarde betekent dat de NaN waarde niet groter is dan, kleiner dan of gelijk is aan een andere double (of float) waarde, met inbegrip NaNvan . Zie het Double.NaN artikel of Single.NaN naslaginformatie voor meer informatie en voorbeelden.
Twee operanden van hetzelfde enumtype zijn gelijk als de bijbehorende waarden van het onderliggende integrale type gelijk zijn.
Door de gebruiker gedefinieerde structtypen ondersteunen de == operator niet standaard. Om de operator te ondersteunen, moet een door de == gebruiker gedefinieerde struct deze overbelasten.
C#- tuples hebben ingebouwde ondersteuning voor de == en != operators. Zie de sectie Tuple-gelijkheid van het artikel Tuple-typen voor meer informatie.
Gelijkheid van verwijzingstypen
Standaard zijn operanden van het verwijzingstype, met uitzondering van records, gelijk als ze naar hetzelfde object verwijzen:
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
}
}
Zoals in het voorgaande voorbeeld wordt weergegeven, ondersteunen door de gebruiker gedefinieerde referentietypen de == operator standaard. Een verwijzingstype kan de == operator echter overbelasten. Als een verwijzingstype de == operator overbelast, gebruikt u de Object.ReferenceEquals methode om te controleren of twee verwijzingen van dat type naar hetzelfde object verwijzen.
Gelijkheid van recordtypen
Recordtypen ondersteunen de == en != operators die standaard semantiek voor gelijkheid van waarden bieden. Dat wil gezegd: twee recordoperanden zijn gelijk wanneer beide waarden of null overeenkomende waarden van alle velden zijn en automatisch geïmplementeerde eigenschappen gelijk zijn.
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
}
}
Zoals in het voorgaande voorbeeld wordt weergegeven, wordt de gelijkheid van leden van het verwijzingstype vergeleken met behulp van hun specifieke gelijkheids-implementaties.
Gelijkheid van tekenreeksen
Twee tekenreeksoperanden zijn gelijk wanneer beide tekenreeksexemplaren of null beide tekenreeksexemplaren dezelfde lengte hebben en identieke tekens hebben in elke tekenpositie:
string s1 = "hello!";
string s2 = "HeLLo!";
Console.WriteLine(s1 == s2.ToLower()); // output: True
string s3 = "Hello!";
Console.WriteLine(s1 == s3); // output: False
Vergelijkingen voor gelijkheid van tekenreeksen zijn hoofdlettergevoelige ordinale vergelijkingen. Zie Tekenreeksen vergelijken in C# voor meer informatie over tekenreeksvergelijking.
Gelijkheid delegeren
Twee operanden van hetzelfde uitvoeringstype zijn gelijk wanneer beide of null hun aanroeplijsten dezelfde lengte hebben en gelijke vermeldingen hebben in elke positie:
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
Belangrijk
Gelijke vermeldingen in een aanroeplijst bevatten alle vaste parameters in de aanroep, inclusief de ontvanger. De ontvanger is het exemplaar van een object dat wordt vertegenwoordigd door this wanneer de vermelding wordt aangeroepen.
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)
Zie de sectie Gelijkheidsoperatoren delegeren van de C#-taalspecificatie voor meer informatie.
Gemachtigden die afkomstig zijn van het evalueren van semantisch identieke lambda-expressies zijn niet gelijk, zoals in het volgende voorbeeld wordt weergegeven:
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
Ongelijkheidsoperator !=
De ongelijkheidsoperator != retourneert true als de operanden niet gelijk zijn en false anders. Voor de operanden van de ingebouwde typen produceert de expressie x != y hetzelfde resultaat als de expressie !(x == y). Zie de sectie Gelijkheidsoperator voor meer informatie over type gelijkheid.
In het volgende voorbeeld ziet u hoe u de != operator gebruikt:
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
Gelijkheid in klassenhiërarchieën
Records verwerken overname correct zonder handmatig werk. Het door de compiler gegenereerde gelijkheid controleert zowel het runtimetype als alle gedeclareerde eigenschappen, zodat deze automatisch voldoet aan de symmetrie- en transitiviteitsvereisten. Geef de voorkeur record aan een handmatige niet-verzegelde hiërarchie wanneer gelijkheid van waarden het doel is.
Belangrijk
Gebruik record waar mogelijk: de compiler genereert alle vereiste gelijkheidsleden voor u. Handmatige implementatie is alleen nodig wanneer uw type moet worden afgeleid van een niet-recordklasse of andere beperkingen heeft die voorkomen record.
Gelijkheid zelf implementeren wanneer een type geen record kan zijn
Hier volgt een minimale handmatige implementatie voor een waardetype dat geen record kan zijn:
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);
}
De implementatie biedt drie vereiste leden: Equals(T?) als kernvergelijking, override Equals(object?) voor aanroepen op objectniveau, zodat override GetHashCode() op hash gebaseerde verzamelingen correct werken.
HashCode.Combine is een bibliotheekhelper waarmee één hash wordt gebouwd op basis van dezelfde waarden die door Equals. Implementatie IEquatable<T> (de Equals(T?) overbelasting) is optioneel, maar vermijdt boksen wanneer bellers al het betontype hebben.
Wanneer u deze ook definieert == en !=, moeten deze als paar worden gebruikt. Waarschuwingen voor CS0660 en CS0661 herinneren u eraan om alle vier de leden consistent te houden.
Met de drie bovenstaande leden weerspiegelt Equals u gelijkheid van waarden, maar == test u nog steeds de identiteit omdat er nog geen == operator is gedeclareerd:
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)
Een juiste uitvoering moet ook voldoen aan het gelijkwaardigheidscontract (stel x, yen z zijn niet null):
-
Reflexief:
x.Equals(x)retourneerttrue. -
Symmetrisch:
x.Equals(y)retourneert dezelfde waarde alsy.Equals(x). -
Transitief: als
x.Equals(y)eny.Equals(z)beidetruezijn, moet datx.Equals(z)zijntrue. -
Consistent: opeenvolgende aanroepen om dezelfde waarde te
x.Equals(y)retourneren zolang geen van beide objecten verandert. -
Null-gedrag:
x.Equals(null)retourneertfalse;x.Equals(y)mag niet worden gegooid wanneer een niet-nullxwordt aangeroepen.
Waarde gelijkheid in een niet-verzegelde klassehiërarchie vereist meer zorg dan in een verzegelde klasse om te voldoen aan de symmetrische en transitieve regels. Het gevaar is dat IEquatable<T>.Equals(T? other) verzending het gedeclareerde type volgt (het type dat is geschreven in de variabeledeclaratie) van de variabele, niet het runtimetype. Als Shape een niet-virtualEquals(Shape? other), variabele wordt gede declareerd die is getypt als Shape die een Circle tijdens runtime aanroept Shape.Equals, wordt -specifieke velden op de achtergrond genegeerd Circle. Twee Circle objecten met verschillende radii kunnen worden vergeleken als gelijk wanneer ze worden geopend via een Shape variabele.
Voor het juiste patroon zijn twee medewerkende vereisten vereist: zorg ervoor dat de getypte methode virtual zodanig is dat Equals elke afgeleide klasse de vergelijking kan uitbreiden en een GetType() == other.GetType() beveiliging kan toevoegen in de basisklasse-implementatie, zodat objecten van verschillende runtimetypen nooit als gelijk worden beschouwd.
Basisklasse-implementatie
// 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);
}
Belangrijkste punten:
-
virtualgetyptEquals: elke afgeleide klasse overschrijft deze methode om de vergelijking met zijn eigen velden te verbeteren. -
GetType()guard:GetType() == other.GetType()voorkomt dat eenCirclegelijk is aan eenShapemet dezelfde kleur en voorkomt dat objecten van verschillende afgeleide typen elkaar gelijkmaken. -
GetHashCodebevatGetType(): omdat twee objecten alleen gelijk zijn wanneer de runtimetypen overeenkomen,GetHashCodemoet het runtime-type en de gegevensvelden worden gehasht. HetGetType()weglaten hier veroorzaakt onjuist gedrag inDictionary<TKey,TValue>enHashSet<T>. -
==gemachtigden voorEquals: houdt operator- en methode-gelijkheid consistent.
Implementatie van afgeleide klassen
Een afgeleide klasse die velden toevoegt, overschrijft het getypte Equals, casts naar een eigen type, aanroepen base.Equalsen vergelijkt vervolgens zijn eigen velden:
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) dwingt de GetType() bewaker af en controleert de gedeelde velden. De cast via other is Circle c mislukt snel wanneer het argument een Shape ander afgeleid type is.
Gebruik via een variabele van het basistype
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)
Verzegelde klassen zijn eenvoudiger
U kunt een sealed klasse niet subklassen, dus compilatietijd en runtimetypen komen altijd overeen. U hebt de GetType() bewaker of virtual verzending niet nodig. Het IEquatable<T> patroon dat wordt weergegeven in Gelijkheid zelf implementeren wanneer een type geen record kan zijn , is juist en volledig voor een verzegelde klasse.
Overbelasting van operatoren
U kunt de operatoren == in een door de != gebruiker gedefinieerd type. Als u een van deze twee operators overbelast, moet u ook de andere operator overbelasten.
U kunt de == operatoren != en operators niet expliciet overbelasten in een recordtype. Als u het gedrag van het == recordtype en != de operators voor recordtype Twilt wijzigen, implementeert u de IEquatable<T>.Equals methode met de volgende handtekening:
public virtual bool Equals(T? other);
C#-taalspecificatie
Zie de sectie Relationele operators en typetests van de C#-taalspecificatie voor meer informatie.
Zie de sectie Gelijkheidsleden van de C#-taalspecificatie voor meer informatie over gelijkheid van recordtypen.