Wir haben bereits Besonderheiten von Structs des .NET-Frameworks analysiert, die Werttypen darstellen, wenn Objekte nach Wert verglichen werden – Instanz von Structs.
Nun werde ich diesen Prozess an einem bestimmten Beispiel beschreiben, um zu prüfen, ob es uns erlaubt, die Verwendung des Objektvergleichs nach Wert allgemein zu bestimmen und damit ein Beispiel für den Vergleich von Objekten nach Wert zu vereinfachen – Klasseninstanzen, die Referenzen darstellen Typen.
Die PersonStruct-Struktur:
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); } }
Wie Sie sehen können, ist dieses Beispiel kleiner und von der Struktur her einfacher, da Instanzen von Strukturen nicht null sind und es nicht möglich ist, von benutzerdefinierten Strukturen zu erben. Besonderheiten bei der Implementierung des Wertvergleichs für die Klasseninstanzen haben wir bereits in meinem vorherigen Artikel besprochen.
Außerdem haben wir Felder für den Objektvergleich festgelegt sowie die Methode GetHashCode() implementiert.
Methoden und Vergleichsoperatoren wurden in der folgenden Reihenfolge implementiert:
- Um zwei Instanzen von Strukturen zu vergleichen, haben wir die statische Methode PersonStruct.Equals(PersonStruct, PersonStruct) implementiert. Wir werden diese Methode als Referenzvergleichsmethode verwenden, wenn wir andere Methoden und Operatoren implementieren. Außerdem kann es angewendet werden, um Instanzen von Strukturen in Sprachen zu vergleichen, die keine Operatoren unterstützen.
- Die Operatoren PersonStruct.==(PersonStruct, PersonStruct) und PersonStruct.!=(PersonStruct, PersonStruct) wurden ebenfalls implementiert. Zu beachten ist, dass ein C#-Compiler folgende Besonderheiten aufweist:
- Sie können mit den überladenen Operatoren T.==(T, T) und T.!=(T, T) in Nullable(Of T) vergleichen
- Vor der Überprüfung einer Wertgleichheit kann ein Compiler überprüfen, ob Instanzen von Strukturen einen gültigen Wert haben. Außerdem schließt ein Compiler Instanzen von Strukturen nicht in Objekte ein.
- Der Vergleich von Instanzen der Struktur Nullable(Of T) mit einem nicht typisierten Nullable-Wert führt also zum Aufrufen der Operatoren ==(T, T) oder T.!=(T, T), während Instanzen der Nullable( Of T)-Struktur ohne überladene Operatoren T.==(T, T) und T.!=(T, T) führt zum Aufruf der Object.==(Object, Object)- oder Object.!=(Object, Object)-Operatoren und als Ergebnis eine Instanz in das Objekt einhüllen.
- Die PersonStruct.Equals(PersonStruct)-Methode (Implementierung von IEquatable(Of PersonStruct)) wurde durch Aufrufen der PersonStruct.Equals(PersonStruct, PersonStruct)-Methode implementiert.
- Um zu vermeiden, dass Instanzen von Strukturen in Objekte verpackt werden, wenn wir eine oder zwei Nullable(Of PersonStruct)-Instanzen haben, ist es möglich, die folgenden Methoden zu implementieren:
- PersonStruct.Equals(PersonStruct?, PersonStruct?) wird als Aufruf des Operators PersonStruct.==(PersonStruct, PersonStruct) verwendet, um zu vermeiden, dass Instanzen von Strukturen beider Argumente in Objekte eingeschlossen und Object.Equals( Object, Object)-Methode, wenn mindestens eines der Argumente eine Nullable(Of PersonStruct)-Instanz ist. Darüber hinaus können Sie diese Methode verwenden, um Nullable(Of PersonStruct)-Instanzen in Sprachen zu vergleichen, die keine Operatoren unterstützen. Im Code finden Sie möglicherweise Kommentare, die erläutern, wie diese Methode implementiert werden könnte, wenn ein C#-Compiler die Operatoren T.==(T, T) und T.!=(T, T) nicht für Nullable(Of T) Argumente.
- PersonStruct.Equals(PersonStruct?) – die Implementierung der IEquatable(Of PersonStruct?)-Schnittstelle, die verwendet wird, um zu vermeiden, dass die Nullable(Of PersonStruct)-Argumente in Objekte verpackt und die PersonStruct.Equals(Object)-Methode aufgerufen werden. Es wird als Aufruf des Operators PersonStruct.==(PersonStruct, PersonStruct) mit dem kommentierten Code implementiert, um die Operatoren T.==(T, T) und T.!=(T, T) für Nullable(Of T) zu verwenden ) Argumente.
- PersonStruct.Equals(Object) – das überschreibt die Methode Object.Equals(Object). Es wird implementiert, indem die Kompatibilität eines Argumenttyps mit einem Typ des aktuellen Objekts mithilfe des is-Operators überprüft wird, indem das Argument in PersonStruct umgewandelt und PersonStruct.Equals(PersonStruct, PersonStruct) aufgerufen wird.
Hinweise:
- Die Implementierung der Schnittstelle IEquatable(Of PersonStruct?) — IEquatable(Of Nullable(Of PersonStruct)) dient dazu, bestimmte Probleme in der Plattform aufzuzeigen, wenn mit Strukturen gearbeitet wird, bei denen das Umschließen von Instanzen in Objekte schneller als erwartet erfolgt.
- In realen Projekten ist die Implementierung von IEquatable(Of Nullable(Of T)) aus Architekturgründen nicht anwendbar, sofern dies nicht zur Verbesserung der Leistung erforderlich ist – wir sollten typisiertes IEquatable nicht für irgendeinen Typ im T-Typ implementieren.
- Im Allgemeinen ist es nicht notwendig, einen Code mit verschiedenen Optimierungen zu überhäufen.
Bei Strukturen könnten wir erreichen, dass der Vergleich nach Wert viel einfacher und produktiver wird, indem wir die Vererbung von benutzerdefinierten Strukturen vermeiden und Objekte auf null prüfen müssen. Außerdem können wir eine neue Logik überwachen, die Nullable(Of T)-Argumente unterstützt.
In meiner zukünftigen Veröffentlichung werde ich die folgenden Punkte zusammenfassen:
- Wenn es eine gute Idee ist, den Vergleich von Objekten nach Wert zu implementieren;
- Wie wir die Implementierung des Vergleichs nach Wert für Objekte vereinfachen können – Klasseninstanzen, die Referenztypen darstellen.
Lesen Sie auch:
Vergleichen von Objekten nach Wert. Teil 1:Anfang
Vergleichen von Objekten nach Wert. Teil 2:Hinweise zur Implementierung der Equals-Methode
Vergleichen von Objekten nach Wert. Teil 3:Typspezifische Gleichheits- und Gleichheitsoperatoren
Vergleichen von Objekten nach Wert. Teil 4:Vererbungs- und Vergleichsoperatoren
Vergleichen von Objekten nach Wert. Teil 5:Problem der Strukturgleichheit