Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Az == (egyenlőség) és != az (egyenlőtlenségi) operátorok ellenőrzik, hogy operandusaik egyenlőek-e vagy sem. Az értéktípusok egyenlők, ha a tartalmaik egyenlőek. A referenciatípusok egyenlők, ha a két változó ugyanarra a tárolóra hivatkozik.
A C# nyelv referenciadokumentuma a C# nyelv legújabb kiadású verzióját ismerteti. Emellett a közelgő nyelvi kiadás nyilvános előzetes verziójú funkcióinak kezdeti dokumentációját is tartalmazza.
A dokumentáció azonosítja azokat a funkciókat, amelyeket először a nyelv utolsó három verziójában vagy az aktuális nyilvános előzetes verziókban vezetnek be.
Jótanács
Ha meg szeretné tudni, hogy mikor jelent meg először egy funkció a C#-ban, tekintse meg a C# nyelvi verzióelőzményeiről szóló cikket.
A mintamegfeleltetési operátort a teszt alternatívájaként is használhatja==, ha állandó értéken tesztel. Az is operátor az alapértelmezett egyenlőségi szemantikát használja az összes érték- és referenciatípushoz.
Egyenlőség operátor ==
Az egyenlőség operátor == akkor ad true vissza értéket, ha az operandusai egyenlőek, false ellenkező esetben.
Értéktípusok egyenlősége
A beépített értéktípusok operandusai egyenlők, ha az értékük egyenlő:
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
Feljegyzés
Ha az ==<><=>= operandusok egyike nem szám (Double.NaN vagy Single.NaN), akkor a művelet falseeredménye . Ez a feltétel azt jelenti, hogy az NaN érték nem nagyobb, mint bármely más double (vagy float) érték, beleértve a NaN. További információkért és példákért tekintse meg vagy tekintse meg a Double.NaNSingle.NaN referenciacikket.
Az azonos számtípusú két operandus egyenlő, ha az alapul szolgáló integráltípus megfelelő értékei egyenlők.
A felhasználó által definiált szerkezettípusok alapértelmezés szerint nem támogatják az == operátort. Az operátor támogatásához a == felhasználó által definiált szerkezetnek túl kell terhelnie azt.
A C#-tuples beépített támogatást nyújt az és == az != operátorok számára. További információkért tekintse meg a Tuple-típusok cikkÉnek Tuple egyenlőségi szakaszát.
Hivatkozástípusok egyenlősége
Alapértelmezés szerint a hivatkozás típusú operandusok, a rekordok kivételével, egyenlőek, ha ugyanarra az objektumra hivatkoznak:
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
}
}
Ahogy az előző példa is mutatja, a felhasználó által definiált referenciatípusok alapértelmezés szerint támogatják az == operátort. A referenciatípus azonban túlterhelheti az operátort == . Ha egy hivatkozástípus túlterheli az == operátort, a Object.ReferenceEquals módszerrel ellenőrizheti, hogy az adott típus két hivatkozása ugyanarra az objektumra hivatkozik-e.
Rekordtípusok egyenlősége
A rekordtípusok támogatják azokat az == operátorokat, != amelyek alapértelmezés szerint értékegyenlőségi szemantikát biztosítanak. Ez azt jelzi, hogy két rekord operandusa egyenlő, ha mindkettő null vagy az összes mező megfelelő értéke, és az automatikusan implementált tulajdonságok egyenlők.
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
}
}
Ahogy az előző példa is mutatja, a referencia típusú tagok egyenlőségét összehasonlítjuk a saját egyenlőségi megvalósításukkal.
Sztring egyenlősége
Két sztringoperndus akkor egyenlő, ha mindkettő null azonos hosszúságú, vagy mindkét sztringpéldány azonos hosszúságú, és mindegyik karakterpozícióban azonos karakterekkel rendelkezik:
string s1 = "hello!";
string s2 = "HeLLo!";
Console.WriteLine(s1 == s2.ToLower()); // output: True
string s3 = "Hello!";
Console.WriteLine(s1 == s3); // output: False
A sztringek egyenlőségének összehasonlítása kis- és nagybetűk közötti összehasonlítás. A sztringek összehasonlításáról további információt a Sztringek összehasonlítása C#-ban című témakörben talál.
Egyenlőség delegálása
Két azonos futásidejű delegált operandus egyenlő, ha mindkettő azonos null hosszúságú, vagy a meghívási listák azonos hosszúságúak, és mindegyik pozícióban azonos bejegyzésekkel rendelkeznek:
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
Fontos
A meghívási lista egyenlő bejegyzései tartalmazzák a hívásban szereplő összes rögzített paramétert, beleértve a fogadót is. A fogadó egy objektum példánya, amelyet this a bejegyzés meghívása jelöl.
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)
További információ: A C# nyelvi specifikációjának Egyenlőségi operátorok delegálása szakasza.
A szemantikailag azonos lambdakifejezések kiértékeléséből származó meghatalmazottak nem egyenlők, ahogyan az alábbi példa is mutatja:
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
Egyenlőtlenség operátor !=
Az egyenlőtlenségi operátor != akkor ad true vissza értéket, ha az operandusai nem egyenlők, és false ellenkező esetben. A beépített típusok operandusai A típusegyenlőségről további információt az Egyenlőség operátor szakaszban talál.
Az alábbi példa bemutatja az operátor használatát != :
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
Egyenlőség az osztályhierarchiákban
A rekordok manuális munka nélkül helyesen kezelik az öröklést. A fordító által létrehozott egyenlőség a futtatókörnyezet típusát és az összes deklarált tulajdonságot is ellenőrzi, így automatikusan megfelel a szimmetrikus és tranzitivitási követelményeknek. Előnyben részesítse record a manuális rendezetlen hierarchiát, ha az értékegyenlőség a cél.
Fontos
Használja record , amikor csak lehetséges – a fordító létrehozza az összes szükséges egyenlőségi tagot. Manuális implementálásra csak akkor van szükség, ha a típusnak nem rekordosztályból kell származnia, vagy más korlátozásokkal kell rendelkeznie, amelyek megakadályozzák record.
Az egyenlőség saját kezű megvalósítása, ha egy típus nem lehet rekord
Íme egy minimális manuális implementáció egy olyan értéktípushoz, amely nem lehet rekord:
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);
}
Az implementáció három szükséges tagot biztosít: Equals(T?) az alapvető összehasonlítást override Equals(object?) az objektumszintű hívásokhoz, és override GetHashCode() így a kivonatalapú gyűjtemények megfelelően működnek.
HashCode.Combine egy olyan kódtár-segéd, amely a függvény által Equalshasznált értékekből készít kivonatot. A megvalósítás IEquatable<T> (a Equals(T?) túlterhelés) nem kötelező, de elkerüli a boxolást, ha a hívók már rendelkeznek a konkrét típussal.
A definiáláskor == és !=a nyelvben párként is szükség van rájuk; a CS0660 és a CS0661 figyelmeztetések figyelmeztetik, hogy mind a négy tag konzisztens maradjon.
A fenti három tag az értékegyenlőséget tükrözi, Equals de == továbbra is teszteli az identitást, mert még nem == deklarált operátort:
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)
A megfelelő végrehajtásnak meg kell felelnie az egyenértékűségi szerződésnek is (feltételezzük x, yés z nem null értékűek):
-
Reflexív:
x.Equals(x)visszaadjatrue. -
Szimmetrikus:
x.Equals(y)ugyanazt az értéket adja vissza, mint ay.Equals(x). -
Tranzitív: ha
x.Equals(y)ésy.Equals(z)mindkettőtrue, akkorx.Equals(z)kell lennietrue. -
Konzisztens: egymást követő hívások, hogy
x.Equals(y)ugyanazt az értéket adja vissza, amíg egyik objektum sem változik. -
Null viselkedés:
x.Equals(null)visszaadjafalse;x.Equals(y)nem szabad dobni, ha nem nullxértékűre van meghívva.
A rendezetlen osztályhierarchiák értékegyenlősége nagyobb körültekintést igényel, mint egy lezárt osztályban a szimmetrikus és tranzitív szabályok kielégítése érdekében. A veszély az, hogy IEquatable<T>.Equals(T? other) a küldés a változó deklarált típusát (a változó deklarációjában írt típust) követi, nem pedig a futtatókörnyezet típusát. Ha Shape nemetEquals(Shape? other)virtual deklarál, a futtatókörnyezetben lévőként ShapeCircle beírt változó meghívja Shape.Equals– csendesen figyelmen kívül hagyva az Circleadott mezőket. Két Circle különböző radiival rendelkező objektum egyenlőként hasonlítható össze, ha egy Shape változón keresztül fér hozzá.
A helyes minta két együttműködési követelményt igényel: a gépelt Equals metódust virtual úgy kell létrehozni, hogy minden származtatott osztály ki tudja terjeszteni az összehasonlítást, és adjon hozzá egy GetType() == other.GetType() védőt az alaposztály implementációjához, így a különböző futtatókörnyezet-típusok objektumai soha nem tekinthetők egyenlőnek.
Alaposztály implementálása
// 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);
}
Főbb pontok:
-
virtualgépeltEquals: minden származtatott osztály felülbírálja ezt a módszert, hogy az összehasonlítást a saját mezőivel egészíthesse ki. -
GetType()őr:GetType() == other.GetType()megakadályozza, hogy egyCircleShapeazonos színű objektum egyenlő legyen, és megakadályozza, hogy a különböző származtatott típusú objektumok egyenlők egymással. -
GetHashCodetartalmazza:GetType()mivel két objektum csak akkor egyenlő, ha a futtatókörnyezet-típusok megegyeznek,GetHashCodea futtatókörnyezet típusát és az adatmezőket is kivonatelni kell. Az itt való kihagyásGetType()helytelen viselkedést okoz az ésHashSet<T>aDictionary<TKey,TValue>. -
==a következőreEqualsdelegáltak: konzisztens marad az operátorok és a metódusok egyenlősége.
Származtatott osztály implementálása
Egy származtatott osztály, amely mezőket ad hozzá, felülbírálja a gépeltet Equals, saját típusra vetíti, meghívja base.Equals, majd összehasonlítja a saját mezőit:
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) kényszeríti az őrt GetType() , és ellenőrzi a megosztott mezőket. A leadás other is Circle c gyorsan meghiúsul, ha az argumentum Shape bármilyen más származtatott típusból áll.
Használat alaptípusú változón keresztül
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)
A lezárt osztályok egyszerűbbek
Az osztályokat nem lehet alosztályba sorolni sealed , ezért a fordítási idő és a futtatókörnyezet típusok mindig megegyeznek. Nincs szükség az őrre vagy virtual a GetType() diszpécserre. Ha IEquatable<T>egy típus nem lehet rekord , akkor a minta akkor jelenik meg, ha egy lezárt osztály esetében egy típus nem lehet helyes és teljes.
Operátorok túlterhelése
A felhasználó által definiált típusban túlterhelheti az operátorokat és == az != operátorokat. Ha a két operátor egyikét túlterheli, akkor a másik operátort is túl kell terhelnie.
A rekordtípusú operátorokat és == operátorokat != nem lehet explicit módon túlterhelni. A rekordtípus ==és != az T operátorok viselkedésének módosításához implementálja a metódust IEquatable<T>.Equals a következő aláírással:
public virtual bool Equals(T? other);
C# nyelvspecifikáció
További információ: A C# nyelvi specifikációjának relációs és típustesztelő operátorok szakasza.
A rekordtípusok egyenlőségével kapcsolatos további információkért tekintse meg a C#-nyelv specifikációjánakEgyenlőség tagjai szakaszát.