Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Operátory == (rovnost) a!= kontrolují, jestli jsou jejich operandy stejné nebo ne. Typy hodnot jsou stejné, pokud se jejich obsah rovná. Odkazové typy jsou stejné, když obě proměnné odkazují na stejné úložiště.
Referenční dokumentace jazyka C# dokumentuje naposledy vydané verze jazyka C#. Obsahuje také počáteční dokumentaci k funkcím ve verzi Public Preview pro nadcházející jazykovou verzi.
Dokumentace identifikuje všechny funkce, které byly poprvé představeny v posledních třech verzích jazyka nebo v aktuálních verzích Public Preview.
Návod
Informace o tom, kdy byla funkce poprvé představena v jazyce C#, najdete v článku o historii verzí jazyka C#.
Operátor porovnávání vzorů is můžete použít jako alternativu == k testu, když testujete proti konstantní hodnotě. Operátor is používá výchozí sémantiku rovnosti pro všechny typy hodnot a odkazů.
Operátor rovnosti ==
Operátor == rovnosti vrátí true , pokud jsou jeho operandy stejné, false jinak.
Rovnost typů hodnot
Operandy předdefinovaných typů hodnot jsou stejné, pokud jsou jejich hodnoty stejné:
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
Poznámka:
==Pro operátory , ,<> , <=a >= operátory, pokud některý z operandů není číslo (Double.NaN nebo Single.NaN), výsledek operace je false. Tato podmínka znamená, že NaN hodnota není větší než, menší než nebo rovna žádné jiné double (nebo float) hodnotě, včetně NaN. Další informace a příklady najdete v článku nebo Double.NaN v referenčním Single.NaN článku.
Dva operandy stejného typu výčtu jsou stejné, pokud jsou odpovídající hodnoty základního celočíselného typu stejné.
Uživatelem definované typy struktur ve výchozím nastavení operátor nepodporují == . Pro podporu operátoru == musí uživatelem definovaná struktura přetížit .
Řazené kolekce členů jazyka C# mají integrovanou podporu pro operátory == a != operátory. Další informace najdete v oddílu rovnosti řazené kolekce členů v článku o typech řazených kolekcí členů.
Rovnost typů odkazů
Ve výchozím nastavení jsou operandy typu odkaz, s výjimkou záznamů, stejné, pokud odkazují na stejný objekt:
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
}
}
Jak ukazuje předchozí příklad, uživatelem definované odkazové typy podporují == operátor ve výchozím nastavení. Typ odkazu však může přetížit == operátor. Pokud typ odkazu přetíží == operátor, pomocí Object.ReferenceEquals metody zkontrolujte, zda dva odkazy tohoto typu odkazují na stejný objekt.
Rovnost typů záznamů
Typy záznamů podporují operátory == , != které ve výchozím nastavení poskytují sémantiku rovnosti hodnot. To znamená, že dva operandy záznamů jsou stejné, pokud jsou null oba nebo odpovídající hodnoty všech polí a automaticky implementované vlastnosti jsou stejné.
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
}
}
Jak ukazuje předchozí příklad, porovnává se rovnost členů typu odkazu pomocí jejich konkrétních implementací rovnosti.
Rovnost řetězců
Dva řetězcové operandy jsou stejné, pokud jsou null obě nebo obě instance řetězců mají stejnou délku a mají stejné znaky v každé pozici znaku:
string s1 = "hello!";
string s2 = "HeLLo!";
Console.WriteLine(s1 == s2.ToLower()); // output: True
string s3 = "Hello!";
Console.WriteLine(s1 == s3); // output: False
Porovnání rovnosti řetězců jsou porovnání s rozlišováním malých a velkých písmen. Další informace o porovnání řetězců naleznete v tématu Porovnání řetězců v jazyce C#.
Delegování rovnosti
Dva operandy delegáta stejného typu běhu jsou stejné, pokud jsou null oba nebo jejich seznamy vyvolání mají stejnou délku a mají stejné položky v každé pozici:
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
Důležité
Stejné položky v seznamu volání zahrnují všechny pevné parametry v vyvolání, včetně příjemce. Příjemce je instance objektu reprezentovaného this při vyvolání položky.
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)
Další informace naleznete v části Delegovat operátory rovnosti specifikace jazyka C#.
Delegáti, kteří pocházejí z vyhodnocení sémanticky identických výrazů lambda , nejsou rovni, jak ukazuje následující příklad:
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
Operátor nerovnosti !=
Operátor != nerovnosti vrátí true , pokud jeho operandy nejsou rovny, a false jinak. Pro operandy předdefinovaných typů výraz x != y vytvoří stejný výsledek jako výraz !(x == y). Další informace o rovnosti typů naleznete v části Operátor rovnosti.
Následující příklad ukazuje použití operátoru != :
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
Rovnost v hierarchiích tříd
Záznamy zpracovávají dědičnost správně bez ruční práce. Vygenerovaná kompilátorem kontroluje rovnost jak typ modulu runtime, tak všechny deklarované vlastnosti, takže automaticky splňuje požadavky na symetrii a tranzitivitu. Preferujte record před ruční nezapečetěnou hierarchií, pokud je cílem rovnost hodnot.
Důležité
Kdykoli record je to možné , kompilátor za vás vygeneruje všechny požadované členy rovnosti. Ruční implementace je nutná pouze v případě, že typ musí být odvozen z třídy bez záznamu nebo má jiná omezení, která brání record.
Implementujte porovnávání rovnosti ručně, když typ nemůže být záznam
Tady je minimální ruční implementace pro typ hodnoty, který nemůže být záznam:
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);
}
Implementace poskytuje tři požadované členy: Equals(T?) jako porovnání jádra, override Equals(object?) pro volání na úrovni objektu a override GetHashCode() proto kolekce založené na hodnotě hash fungují správně.
HashCode.Combine je pomocník knihovny, který vytváří jednu hodnotu hash ze stejných hodnot, které Equalspoužívá . Implementace IEquatable<T> (přetížení) je volitelná, Equals(T?) ale zabraňuje boxování, pokud volající již mají konkrétní typ.
Když také definujete == a !=jazyk je vyžaduje jako pár. Upozornění CS0660 a CS0661 vám připomenou, abyste měli všechny čtyři členy konzistentní.
S výše uvedenými třemi členy odráží rovnost hodnot, ale == stále testuje identitu, Equals protože ještě nebyl deklarován žádný == operátor:
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)
Správná implementace musí také splňovat smlouvu o ekvivalenci (předpokládat x, ya z jsou nenulové):
-
Reflexivní:
x.Equals(x)vrátítrue. -
Symetrické:
x.Equals(y)vrátí stejnou hodnotu jakoy.Equals(x). -
Tranzitivní: pokud
x.Equals(y)a jsou obatruey.Equals(z), pakx.Equals(z)musí býttrue. -
Konzistentní: následná volání, která
x.Equals(y)vrátí stejnou hodnotu, pokud se žádný objekt nezmění. -
Chování null:
x.Equals(null)vrátífalse;x.Equals(y)nesmí vyvolat při zavolání na non-nullx.
Rovnost hodnot v nezapečetěné hierarchii tříd vyžaduje větší péči než v zapečetěné třídě, aby vyhovovala symetrickým a tranzitivním pravidlům. Nebezpečí je, že IEquatable<T>.Equals(T? other) odeslání následuje deklarovaný typ (typ napsaný v deklaraci proměnné) proměnné, nikoli jeho typ modulu runtime. Pokud Shape deklaruje nevirtualEquals(Shape? other), proměnná typu, která Shape obsahuje Circle vyvolání Shape.Equalsza běhu – bezobslužné ignorování Circlepolí specifických pro konkrétní pole. Dva Circle objekty s různými paprsky se můžou při přístupu prostřednictvím Shape proměnné porovnávat jako stejné.
Správný vzor vyžaduje dva spolupracující požadavky: nastavte typovou metoduvirtual, aby každá odvozená Equals třída může rozšířit porovnání, a přidat GetType() == other.GetType() ochranu v implementaci základní třídy, takže objekty různých typů modulu runtime se nikdy nepovažují za stejné.
Implementace základní třídy
// 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);
}
Klíčové body:
-
virtualtyped : každá odvozenáEqualstřída přepíše tuto metodu pro rozšíření porovnání s vlastními poli. -
GetType()guard:GetType() == other.GetType()zabraňujeCircletomu, aby se rovnalShapestejné barvě, a zabraňuje objektům různých odvozených typů, aby se navzájem rovnaly. -
GetHashCodezahrnujeGetType(): protože dva objekty jsou stejné pouze v případě, že se jejich typy modulu runtime shodují,GetHashCodemusí hashovat typ modulu runtime a také datová pole. VynecháníGetType()zde způsobí nesprávné chování aDictionary<TKey,TValue>HashSet<T>. -
==delegáty naEquals: udržuje operátor a rovnost metod konzistentní.
Implementace odvozené třídy
Odvozená třída, která přidává pole přepíše typ Equals, přetypuje na svůj vlastní typ, volání base.Equalsa pak porovná své vlastní pole:
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) vynucuje GetType() ochranu a kontroluje sdílená pole. Přetypování other is Circle c selže rychle, pokud je argumentem Shape jakýkoli jiný odvozený typ.
Použití prostřednictvím proměnné základního typu
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)
Zapečetěné třídy jsou jednodušší
Třídu nemůžete podtřídět sealed , takže typy runtime a čas kompilace vždy souhlasí. Nepotřebujete stráže GetType() ani virtual dispečink. Vzor IEquatable<T> zobrazený v implementaci rovnosti sami, když typ nemůže být záznam správný a dokončený pro zapečetěnou třídu.
Přetížení operátoru
Operátory a operátory == můžete != v uživatelem definovaném typu. Pokud přetížíte jeden z těchto dvou operátorů, musíte také přetížit druhý operátor.
Operátory a == operátory != v typu záznamu nelze explicitně přetížit. Chcete-li změnit chování a == operátory != pro typ Tzáznamu , implementujte metodu IEquatable<T>.Equals s následujícím podpisem:
public virtual bool Equals(T? other);
specifikace jazyka C#
Další informace najdete v části Relační a typové operátory specifikace jazyka C#.
Další informace o rovnosti typů záznamů najdete v části Členové rovnostispecifikace jazyka C#.