Vi har allerede analyseret ejendommeligheder ved strukturer i .NET-rammeværket, der repræsenterer værdityper, når man sammenligner objekter efter værdi – forekomst af strukturer.
Nu vil jeg beskrive denne proces på et bestemt eksempel for at kontrollere, om det vil give os mulighed for at bestemme brugen af objektsammenligningen efter værdi generelt og dermed forenkle en prøve af sammenligning af objekter efter værdi - klasseforekomster, der repræsenterer reference typer.
PersonStruct-strukturen:
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); } }
Som du kan se, er dette eksempel mindre og lettere af struktur, da forekomster af strukturer ikke er nul, og det ikke er muligt at nedarve fra brugerdefinerede strukturer. Vi har allerede diskuteret særegenheder for at implementere sammenligningen efter værdi for klasseforekomsterne i min tidligere artikel.
Derudover har vi bestemt felter til objektsammenligning samt implementeret GetHashCode()-metoden.
Metoder og operatører til sammenligning er blevet implementeret i følgende rækkefølge:
- For at sammenligne to forekomster af strukturer har vi implementeret den statiske metode PersonStruct.Equals(PersonStruct, PersonStruct). Vi vil bruge denne metode som referencesammenligningsmetode, når vi implementerer andre metoder og operatører. Derudover kan den anvendes til at sammenligne forekomster af strukturer på sprog, der ikke understøtter operatører.
- PersonStruct.==(PersonStruct, PersonStruct) og PersonStruct.!=(PersonStruct, PersonStruct) operatørerne er også blevet implementeret. Det skal bemærkes, at en C#-compiler har følgende ejendommeligheder:
- Du kan sammenligne med de overbelastede operatorer T.==(T, T) og T.!=(T, T) i Nullable(Of T)
- Før kontrol af en værdilighed, kan en compiler kontrollere, om forekomster af strukturer har en gyldig værdi. Derudover pakker en compiler ikke forekomster af strukturer ind i objekter.
- Således fører sammenligning af forekomster af Nullable(Of T)-strukturen med en ikke-typebestemt nulværdi til at kalde ==(T, T)- eller T.!=(T, T)-operatorerne, mens man sammenligner forekomster af Nullable( Af T) struktur uden overbelastede operatorer T.==(T, T) og T.!=(T, T) resulterer i at kalde operatørerne Objekt.==(Objekt, Objekt) eller Objekt.!=(Objekt, Objekt) og som et resultat, at en instans pakkes ind i objektet.
- PersonStruct.Equals(PersonStruct)-metoden (implementering af IEquatable(Of PersonStruct)) er blevet implementeret ved at kalde metoden PersonStruct.Equals(PersonStruct, PersonStruct).
- For at undgå at pakke forekomster af strukturer ind i objekter, når vi har en eller to Nullable(Of PersonStruct) forekomster, er det muligt at implementere følgende metoder:
- PersonStruct.Equals(PersonStruct?, PersonStruct?), som et kald af operatøren PersonStruct.==(PersonStruct, PersonStruct) bruges til at undgå at ombryde forekomster af strukturer af begge argumenter til objekter og kalde Object.Equals( Object, Object) metode, hvis mindst et af argumenterne er en Nullable(Of PersonStruct)-instans. Derudover kan du bruge denne metode til at sammenligne Nullable(Of PersonStruct)-forekomster på sprog, der ikke understøtter operatører. I koden kan du finde kommentarer, der forklarer, hvordan denne metode kunne implementeres, hvis en C#-kompiler ikke var i stand til at bruge T.==(T, T)- og T.!=(T, T)-operatorerne til Nullable(Of) T) argumenter.
- PersonStruct.Equals(PersonStruct?) – implementeringen af IEquatable(Of PersonStruct?)-grænsefladen, der bruges til at undgå at pakke Nullable(Of PersonStruct)-argumenterne ind i objekter og kalde PersonStruct.Equals(Object)-metoden. Det er implementeret som et kald af PersonStruct.==(PersonStruct, PersonStruct)-operatøren med den kommenterede kode for at bruge T.==(T, T) og T.!=(T, T)-operatorerne til Nullable(Of T) ) argumenter.
- PersonStruct.Equals(Object) – der tilsidesætter Object.Equals(Object)-metoden. Det implementeres ved at kontrollere kompatibiliteten af en argumenttype med en type af det aktuelle objekt ved hjælp af is-operatoren ved at caste argumentet til PersonStruct og kalde PersonStruct.Equals(PersonStruct, PersonStruct).
Bemærkninger:
- Implementeringen af IEquatable(Of PersonStruct?) — IEquatable(Of Nullable(Of PersonStruct))-grænsefladen tjener til at vise særlige problemer i platformen, når man arbejder med strukturer, hvor indpakning af forekomster i objekter sker hurtigere, end vi forventer.
- I virkelige projekter, forudsat at det ikke er nødvendigt at forbedre ydeevnen, er implementeringen af IEquatable(Of Nullable(Of T)) ikke anvendelig af arkitekturmæssige årsager – vi bør ikke implementere typet IEquatable i T-typen for nogen type.
- Generelt er det ikke nødvendigt at overvælde en kode med forskellige optimeringer.
For strukturer kunne vi opnå at gøre sammenligningen efter værdi meget enklere og mere produktiv ved at undgå nedarvning af brugerdefinerede strukturer og behov for at kontrollere objekter på null. Derudover kan vi overvåge en ny logik, der understøtter Nullable(Of T) argumenter.
I min fremtidige publikation vil jeg opsummere følgende punkter:
- Når det er en god idé at implementere sammenligning af objekter efter værdi;
- Hvordan vi kan forenkle implementeringen af sammenligning efter værdi for objekter – klasseforekomster, der repræsenterer referencetyper.
Læs også:
Sammenligning af objekter efter værdi. Del 1:Begyndelse
Sammenligning af objekter efter værdi. Del 2:Implementeringsnotater af Equals-metoden
Sammenligning af objekter efter værdi. Del 3:Typespecifikke Equals og Equality Operators
Sammenligning af objekter efter værdi. Del 4:Arv- og sammenligningsoperatører
Sammenligning af objekter efter værdi. Del 5:Structure Equality Issue