Již jsme analyzovali zvláštnosti struktur rozhraní .NET, které reprezentují typy hodnot při porovnávání objektů podle hodnoty – instance struktur.
Nyní popíšu tento proces na konkrétním příkladu, abychom si ověřili, zda nám umožní obecně určit použití porovnávání objektů podle hodnoty, a tím zjednodušit vzorek porovnávání objektů podle hodnoty – instance třídy, které představují referenci. typy.
Struktura PersonStruct:
using System; namespace HelloEquatable { public struct PersonStruct : IEquatable<PersonStruct>, IEquatable<PersonStruct?> { private static int GetHashCodeHelper(int[] subCodes) { int result = subCodes[0]; for (int i = 1; i < subCodes.Length; i++) result = unchecked(result * 397) ^ subCodes[i]; return result; } private static string NormalizeName(string name) => name?.Trim() ?? string.Empty; private static DateTime? NormalizeDate(DateTime? date) => date?.Date; public string FirstName { get; } public string LastName { get; } public DateTime? BirthDate { get; } public PersonStruct(string firstName, string lastName, DateTime? birthDate) { this.FirstName = NormalizeName(firstName); this.LastName = NormalizeName(lastName); this.BirthDate = NormalizeDate(birthDate); } public override int GetHashCode() => GetHashCodeHelper( new int[] { this.FirstName.GetHashCode(), this.LastName.GetHashCode(), this.BirthDate.GetHashCode() } ); public static bool Equals(PersonStruct first, PersonStruct second) => first.BirthDate == second.BirthDate && first.FirstName == second.FirstName && first.LastName == second.LastName; public static bool operator ==(PersonStruct first, PersonStruct second) => Equals(first, second); public static bool operator !=(PersonStruct first, PersonStruct second) => !Equals(first, second); public bool Equals(PersonStruct other) => Equals(this, other); public static bool Equals(PersonStruct? first, PersonStruct? second) => first == second; // Alternate version: //public static bool Equals(PersonStruct? first, PersonStruct? second) => // first.HasValue == second.HasValue && // ( // !first.HasValue || Equals(first.Value, second.Value) // ); public bool Equals(PersonStruct? other) => this == other; // Alternate version: //public bool Equals(PersonStruct? other) => // other.HasValue && Equals(this, other.Value); public override bool Equals(object obj) => (obj is PersonStruct) && Equals(this, (PersonStruct)obj); // Alternate version: //public override bool Equals(object obj) => // obj != null && // this.GetType() == obj.GetType() && // Equals(this, (PersonStruct)obj); } }
Jak vidíte, tento příklad je menší a jednodušší strukturou, protože instance struktur nejsou null a není možné dědit z uživatelsky definovaných struktur. Již jsme diskutovali o zvláštnostech implementace porovnání podle hodnoty pro instance třídy v mém předchozím článku.
Kromě toho jsme určili pole pro porovnání objektů a implementovali metodu GetHashCode().
Metody a operátory porovnání byly implementovány v následujícím pořadí:
- Pro porovnání dvou instancí struktur jsme implementovali statickou metodu PersonStruct.Equals(PersonStruct, PersonStruct). Tuto metodu použijeme jako referenční srovnávací metodu při implementaci jiných metod a operátorů. Navíc jej lze použít k porovnání instancí struktur v jazycích, které nepodporují operátory.
- Byly implementovány také operátory PersonStruct.==(PersonStruct, PersonStruct) a PersonStruct.!=(PersonStruct, PersonStruct). Je třeba poznamenat, že kompilátor C# má následující zvláštnosti:
- Můžete porovnat s přetíženými operátory T.==(T, T) a T.!=(T, T) v Nullable(Of T)
- Před kontrolou rovnosti hodnot může kompilátor ověřit, zda instance struktur mají platnou hodnotu. Kompilátor navíc nezabaluje instance struktur do objektů.
- Porovnávání instancí struktury Nullable(Of T) s nezadanou hodnotou s možností null vede k volání operátorů ==(T, T) nebo T.!=(T, T), zatímco porovnáváme instance Nullable( Struktura T) bez přetížených operátorů T.==(T, T) a T.!=(T, T) má za následek volání operátorů Object.==(Object, Object) nebo Object.!=(Object, Object) a v důsledku toho při zabalení instance do objektu.
- Metoda PersonStruct.Equals(PersonStruct) (implementace IEquatable(Of PersonStruct)) byla implementována voláním metody PersonStruct.Equals(PersonStruct, PersonStruct).
- Aby se předešlo zalamování instancí struktur do objektů, když máme jednu nebo dvě instance s možností Null(Of PersonStruct), je možné implementovat následující metody:
- PersonStruct.Equals(PersonStruct?, PersonStruct?) jako volání operátoru PersonStruct.==(PersonStruct, PersonStruct) se používá k tomu, aby se zabránilo zalamování instancí struktur obou argumentů do objektů a volání Object.Equals( Object, Object), pokud je alespoň jeden z argumentů instancí Nullable(Of PersonStruct). Kromě toho můžete tuto metodu použít k porovnání instancí Nullable(Of PersonStruct) v jazycích, které nepodporují operátory. V kódu můžete najít komentáře vysvětlující, jak by tato metoda mohla být implementována, pokud kompilátor C# nebyl schopen použít operátory T.==(T, T) a T.!=(T, T) pro Nullable(Of T) argumenty.
- PersonStruct.Equals(PersonStruct?) – implementace rozhraní IEquatable(Of PersonStruct?), které se používá k tomu, aby se zabránilo zabalení argumentů Nullable(Of PersonStruct) do objektů a volání metody PersonStruct.Equals(Object). Je implementován jako volání operátoru PersonStruct.==(PersonStruct, PersonStruct) s komentovaným kódem pro použití operátorů T.==(T, T) a T.!=(T, T) pro Nullable(Of T) ) argumenty.
- PersonStruct.Equals(Object) – přepíše metodu Object.Equals(Object). Je implementován kontrolou kompatibility typu argumentu s typem aktuálního objektu pomocí operátoru is přetypováním argumentu do PersonStruct a voláním PersonStruct.Equals(PersonStruct, PersonStruct).
Poznámky:
- Implementace rozhraní IEquatable(Of PersonStruct?) — IEquatable(Of Nullable(Of PersonStruct)) slouží k zobrazení konkrétních problémů na platformě při práci se strukturami, kdy k zalamování instancí do objektů dochází rychleji, než očekáváme.
- V reálných projektech, za předpokladu, že není nutné zlepšit výkon, není implementace IEquatable(Of Nullable(Of T)) z architektonických důvodů použitelná – neměli bychom implementovat typované IEquatable v typu T pro žádný typ.
- Obecně není nutné kód zahltit různými optimalizacemi.
U struktur bychom mohli dosáhnout mnohem jednoduššího a produktivnějšího porovnání podle hodnoty tím, že se vyhneme dědění uživatelsky definovaných struktur a nutnosti kontrolovat objekty na null. Kromě toho můžeme sledovat novou logiku, která podporuje argumenty Nullable(Of T).
Ve své budoucí publikaci shrnu následující body:
- kdy je dobré implementovat porovnání objektů podle hodnoty;
- Jak můžeme zjednodušit implementaci porovnání podle hodnoty pro objekty – instance tříd, které představují referenční typy.
Přečtěte si také:
Porovnávání objektů podle hodnoty. Část 1:Začátek
Porovnávání objektů podle hodnoty. Část 2:Poznámky k implementaci metody Equals
Porovnávání objektů podle hodnoty. Část 3:Typově specifické operátory rovnosti a rovnosti
Porovnávání objektů podle hodnoty. Část 4:Operátory dědičnosti a porovnávání
Porovnávání objektů podle hodnoty. Část 5:Problém rovnosti struktur