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.
Tip
Tento článek je součástí části Základy pro vývojáře, kteří už znají aspoň jeden programovací jazyk a učí se jazyk C#. Pokud s programováním teprve začínáte, začněte nejprve kurzy Začínáme .
Pochází z jiného jazyka? V Javě == a v JavaScriptu === testují u objektů identitu, nikoli obsah. Třídy jazyka C# fungují ve výchozím nastavení stejným způsobem. V Pythonu == volá __eq__ a ve výchozím nastavení porovnává obsah, podobně jako se porovnávají záznamy v C#.
Struktury jazyka C# se také porovnávají podle hodnoty při volání Equals.
Jazyk C# rozlišuje dva druhy rovnosti. Rovnost hodnot znamená, že dvě instance jsou stejné, když se jejich data shodují. Rovnost odkazu znamená, že dvě proměnné jsou stejné pouze v okamžiku, kdy odkazují na stejný objekt v paměti. Tato podmínka se také nazývá identita. Typy hodnot obvykle porovnávají data a referenční typy obvykle porovnávají identitu. Autoři typů můžou tyto výchozí hodnoty změnit, ale tento mentální model zabraňuje drobným chybám, kdy dva objekty, které vypadají stejně, nejsou považovány za stejné nebo kde se mítnutí pomocí jedné proměnné tiše změní, co vidí jiná proměnná.
Typy hodnot, referenční typy a výchozí nastavení rovnosti
Každý typ v jazyce C# je typ hodnoty nebo typ odkazu. Typ hodnoty uchovává data přímo v proměnné. Typ odkazu obsahuje odkaz na objekt. Když přiřadíte proměnnou typu odkaz na jinou proměnnou, obě proměnné odkazují na stejný objekt. Další informace o typech hodnot a odkazových typech naleznete v tématu Přehled systému typů.
Výchozí chování rovnosti se obvykle řídí typem:
-
Předdefinované číselné typy a výčty jsou typy hodnot. Dvě
intproměnné jsou stejné, když se jejich číselné hodnoty shodují. -
Struktury jsou typy hodnot. Prostý
structpoužívá rovnost hodnot při volání Equals. - Tice jsou hodnotové typy. Dvě n-tice jsou si rovny, pokud se shodují všechny hodnoty jejich prvků.
-
Třídy jsou odkazové typy. Obyčejná třída používá porovnávání podle reference, takže
==a Equals testují, zda dvě proměnné ukazují na stejný objekt.
Třída používá porovnávání referencí. Dva samostatné objekty se stejnými daty se nerovnají, ale dvě proměnné, které odkazují na stejný objekt, jsou stejné:
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
A struct zobrazuje rovnost hodnot prostřednictvím Equals. Dvě instance struktury jsou stejné, když se jejich pole shodují:
var pt1 = new Point(3, 4);
var pt2 = new Point(3, 4);
Console.WriteLine(pt1.Equals(pt2)); // => True
Prosté struktury nemají předdefinovaný operátor ==. Zápis p1 == p2 do prosté struktury se zkompiluje pouze v případě, že struktura deklaruje své vlastní operator ==. Pokud pro strukturu potřebujete relační operátory, definujte == a != jako dvojici a zachovejte jejich konzistenci s Equals a GetHashCode.
Ntice jsou také hodnotové typy. Dvě n-tice jsou si rovny, pokud se shoduje hodnota každého prvku. Názvy prvků v pojmenované n-tici slouží pouze pro pohodlí při kompilaci a při porovnávání se neberou v úvahu. Záleží jenom na pozicích a hodnotách:
var t1 = (Name: "Grace", Role: "Engineer");
var t2 = (Name: "Grace", Role: "Engineer");
Console.WriteLine(t1 == t2); // => True
Další informace o syntaxi řazených kolekcí členů a dekonstrukci naleznete v tématu Řazené kolekce členů a dekonstrukce.
Použití Object.ReferenceEquals k přímému otestování identity
ReferenceEquals vždy testuje identitu bez ohledu na to, jakým způsobem typ přepíše Equals nebo přetíží ==. Použijte ji jako diagnostiku identity, když potřebujete ověřit, jestli dvě proměnné odkazují na stejný objekt:
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
Běžné použití je uvnitř přepsání metody Equals, aby se úplné porovnání mohlo přeskočit: pokud oba argumenty odkazují na stejnou instanci, jsou vždy považovány za rovné, aniž by bylo nutné kontrolovat jednotlivá pole.
Note
Pokud jsou proměnné zadány jako rozhraní, zkontroluje, == zda proměnné rozhraní odkazují na stejný objekt. Volání Equals stále spouští implementaci základního objektu.
Note
ReferenceEquals Vždy se vrátí false při porovnávání hodnotových typů, i když oba argumenty obsahují stejné hodnoty. K tomuto chování dochází, protože každý argument typu hodnoty je při předání metodě ReferenceEquals nezávisle zabalen do samostatného objektu v haldě.
Typy mohou definovat různé sémantiky rovnosti.
Typy mohou definovat sémantiku rovnosti, která se liší od výchozího chování. Nejběžnějším důvodem je implementace rovnosti hodnot. Pokud vytvoříte typ, který představuje data, jako je bankovní účet, produkt v inventáři nebo uživatel v systému, zvažte instance se stejnými hodnotami jako stejné. Zvolte typy záznamů pro implementaci rovnosti hodnot a kompilátor vygeneruje všechny potřebné členy rovnosti za vás.
Note
Řetězce jsou třídy, ale ==Equals porovnávají obsah řetězců, nikoli identitu.
-
==: operátor rovnosti. Většina typů používá tento operátor jako primární kontrolu rovnosti. Jeho chování závisí na tom, jestli má typ integrovaný nebo uživatelem definovaný==operátor. -
!=: operátor nerovnosti. Pokud typ definuje uživatelem definovaný==operátor, musí také definovat!=. - Equals: virtuální metoda děděná všemi typy. Můžete ji předefinovat a tím změnit sémantiku rovnosti pro daný typ.
- GetHashCode: virtuální metoda používaná kolekcemi založenými na hashování. Pokud jsou dvě hodnoty stejné, musí být jejich kódy hash také stejné.
- ReferenceEquals: statická metoda, která vždy testuje identitu.
Použití záznamů pro rovnost hodnot
Pomocí modifikátoru record určíte, že typ zaměřený na data má rovnost podle hodnoty, pokud může být záznamem. Kompilátor generuje Equals, GetHashCodea ==/!= členy, které porovnávají každou deklarovanou hodnotu vlastnosti.
A record class je stále referenčním typem, ale porovnává hodnoty místo identity:
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 potvrzuje, že person1 a person2 jsou různé objekty v paměti, zatímco == a Equals vracejí True, protože operátor rovnosti generovaný kompilátorem porovnává hodnoty vlastností.
Stejné generování kompilátoru platí pro record struct typy:
var dim1 = new Dimension(1920, 1080);
var dim2 = new Dimension(1920, 1080);
Console.WriteLine(dim1 == dim2); // => True
Console.WriteLine(dim1.Equals(dim2)); // => True
Typy záznamů generují celou sadu rovnosti pro svůj vlastní typ. Typy record class i record struct přepisují Equals a GetHashCode. Také generují == a != operátory a také typovanou Equals metodu pro typ záznamu. Na rozdíl od prostého struct, proto record struct podporuje == a != automaticky. Další informace o typech záznamů a jejich sémantice rovnosti naleznete v tématu Záznamy.
Záznamy se členy referenčního typu
Rovnost záznamů používá vlastní sémantiku rovnosti členů. Každá vlastnost nebo pole se porovnává pomocí vlastní Equals metody. U většiny skalárních hodnot, například int, stringnebo DateTime, tento přístup porovnává hodnoty členů záznamů. Nuance nastává u běžných proměnlivých kolekcí, jako jsou List<T> nebo T[]: tyto typy se porovnávají podle reference, takže dvě instance recordů, které obsahují různé objekty typu seznam se stejným obsahem, nejsou podle syntetizované rovnosti recordů považovány za stejné.
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 a playlist2 jsou samostatné List<string> instance. I když se jejich obsah shoduje, Equals vrátí false.
Pokud potřebujete, aby se v obsahu kolekce projevila rovnost záznamů, máte několik možností:
-
Implementujte
IEquatable<T>v záznamu a přepišteEquals, aby se pro členy kolekce použilo Enumerable.SequenceEqual. -
Použijte typ kolekce s porovnáváním podle hodnoty – například vlastní
IEqualityComparer<T>nebo typ, jehož vlastníEqualsporovnává prvky. - Návrh na identitu: Pokud záznam představuje entitu místo čisté hodnoty, může být rovnost odkazů pro členy kolekce úmyslná.
Important
Ruční implementace rovnosti je dnes v jazyce C# vzácná. Záznamy zpracovávají běžný scénář rovnosti hodnot automaticky. Pokud potřebujete implementovat rovnost ručně – například protože váš typ musí být odvozen z nezáznamové základní třídy – podívejte se do referenčních informací k jazyku na článek Implementace rovnosti, pokud typ nemůže být záznamem.
Viz také
- Přehled systému typů
- Třídy
- Struktury
- Záznamy
- Řazené n-tice a dekonstukce.
- Operátory rovnosti (referenční informace k jazyku)
- Rovnost v hierarchiích tříd – pokročilé pokyny k polymorfní rovnosti
- Aritmetické, relační, logické a přiřazovací operátory – přehled operátoru rovnosti spolu s aritmetickými, relačními, logickými a přiřazovacími operátory.