Database
 sql >> Database >  >> RDS >> Database

Confronto di oggetti per valore. Parte 6:Implementazione dell'uguaglianza della struttura

Abbiamo già analizzato le peculiarità degli struct del framework .NET che rappresentano i tipi di valore quando si confrontano gli oggetti per valore – istanza di struct.

Ora, descriverò questo processo su un esempio particolare per verificare se ci consentirà di determinare l'uso del confronto di oggetti per valore in generale e quindi, per semplificare un esempio di confronto di oggetti per valore:istanze di classe che rappresentano un riferimento tipi.

La struttura 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);
    }
}

Come puoi vedere, questo esempio è più piccolo e più semplice per struttura, poiché le istanze di struct non sono null e non è possibile ereditare da struct definiti dall'utente. Abbiamo già discusso le peculiarità per implementare il confronto per valore per le istanze di classe nel mio precedente articolo.

Inoltre, abbiamo determinato i campi per il confronto degli oggetti e implementato il metodo GetHashCode().

I metodi e gli operatori di confronto sono stati implementati nel seguente ordine:

  1. Per confrontare due istanze di struct, abbiamo implementato il metodo statico PersonStruct.Equals(PersonStruct, PersonStruct). Useremo questo metodo come metodo di confronto di riferimento quando implementeremo altri metodi e operatori. Inoltre, può essere applicato per confrontare istanze di struct in linguaggi che non supportano gli operatori.
  2. Anche gli operatori PersonStruct.==(PersonStruct, PersonStruct) e PersonStruct.!=(PersonStruct, PersonStruct) sono stati implementati. Va notato che un compilatore C# ha le seguenti particolarità:
  • Puoi confrontare con gli operatori sovraccaricati T.==(T, T) e T.!=(T, T) in Nullable(Of T)
  • Prima di controllare un'uguaglianza di valori, un compilatore può verificare se le istanze di struct hanno un valore valido. Inoltre, un compilatore non esegue il wrapping di istanze di struct in oggetti.
  • Quindi, confrontare le istanze della struttura Nullable(Of T) con un valore nullable non tipizzato porta a chiamare gli operatori ==(T, T) o T.!=(T, T), mentre si confrontano le istanze di Nullable( Di T) struttura senza operatori sovraccaricati T.==(T, T) e T.!=(T, T) comporta la chiamata degli operatori Object.==(Oggetto, Oggetto) o Oggetto.!=(Oggetto, Oggetto) e, di conseguenza, avvolgendo un'istanza nell'oggetto.
  1. Il metodo PersonStruct.Equals(PersonStruct) (implementazione di IEquatable(Of PersonStruct)) è stato implementato chiamando il metodo PersonStruct.Equals(PersonStruct, PersonStruct).
  2. Per evitare il wrapping di istanze di struct in oggetti, quando abbiamo una o due istanze Nullable(Of PersonStruct), è possibile implementare i seguenti metodi:
  • PersonStruct.Equals(PersonStruct?, PersonStruct?), come chiamata dell'operatore PersonStruct.==(PersonStruct, PersonStruct), viene utilizzato per evitare il wrapping di istanze di struct di entrambi gli argomenti in oggetti e chiamare Object.Equals( Object, Object) se almeno uno degli argomenti è un'istanza Nullable(Of PersonStruct). Inoltre, puoi usare questo metodo per confrontare le istanze Nullable(Of PersonStruct) in lingue che non supportano gli operatori. Nel codice è possibile trovare commenti che spiegano come implementare questo metodo se un compilatore C# non è stato in grado di utilizzare gli operatori T.==(T, T) e T.!=(T, T) per Nullable(Of T) argomenti.
  • PersonStruct.Equals(PersonStruct?) – l'implementazione dell'interfaccia IEquatable(Of PersonStruct?) utilizzata per evitare il wrapping degli argomenti Nullable(Of PersonStruct) in oggetti e chiamare il metodo PersonStruct.Equals(Object). Viene implementato come una chiamata dell'operatore PersonStruct.==(PersonStruct, PersonStruct) con il codice commentato per utilizzare gli operatori T.==(T, T) e T.!=(T, T) per Nullable(Of T ) argomenti.
  • PersonStruct.Equals(Object) – che sovrascrive il metodo Object.Equals(Object). Viene implementato verificando la compatibilità di un tipo di argomento con un tipo dell'oggetto corrente utilizzando l'operatore is eseguendo il cast dell'argomento in PersonStruct e chiamando PersonStruct.Equals(PersonStruct, PersonStruct).

Note:

  • L'implementazione dell'interfaccia IEquatable(Of PersonStruct?) — IEquatable(Of Nullable(Of PersonStruct)) serve a mostrare problemi particolari nella piattaforma quando si lavora con struct in cui il wrapping delle istanze negli oggetti avviene più velocemente del previsto.
  • Nei progetti reali, a condizione che non sia necessario migliorare le prestazioni, l'implementazione di IEquatable(Of Nullable(Of T)) non è applicabile per motivi di architettura:non dovremmo implementare IEquatable tipizzato nel tipo T per nessun tipo.
  • In generale, non è necessario sovraccaricare un codice con ottimizzazioni diverse.

Per le strutture, potremmo riuscire a rendere il confronto per valore molto più semplice e produttivo evitando l'ereditarietà di strutture definite dall'utente e la necessità di controllare gli oggetti su null. Inoltre, possiamo monitorare una nuova logica che supporta argomenti Nullable(Of T).

Nella mia futura pubblicazione, riassumerò i seguenti punti:

  • Quando è una buona idea implementare il confronto degli oggetti per valore;
  • Come possiamo semplificare l'implementazione del confronto per valore per gli oggetti:istanze di classi che rappresentano tipi di riferimento.

Leggi anche:

Confronto di oggetti per valore. Parte 1:Inizio

Confronto di oggetti per valore. Parte 2:Note di attuazione del metodo Equals

Confronto di oggetti per valore. Parte 3:Operatori di uguaglianza e uguaglianza specifici per tipo

Confronto di oggetti per valore. Parte 4:Operatori di eredità e confronto

Confronto di oggetti per valore. Parte 5:Problema di uguaglianza della struttura