Database
 sql >> Base de Dados >  >> RDS >> Database

Comparando objetos por valor. Parte 6:Implementação da Igualdade Estrutural


Já analisamos peculiaridades de structs do framework .NET que representam Value Types ao comparar objetos por valor – instância de structs.

Agora, vou descrever esse processo em um exemplo específico para verificar se ele nos permitirá determinar o uso da comparação de objetos por valor em geral e, assim, simplificar uma amostra de comparação de objetos por valor – instâncias de classe que representam referência tipos.



A estrutura 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);
    }
}

Como você pode ver, este exemplo é menor e mais fácil por estrutura, pois instâncias de structs não são nulas e não é possível herdar de structs definidas pelo usuário. Já discutimos peculiaridades para implementar a comparação por valor para as instâncias de classe em meu artigo anterior.

Além disso, determinamos campos para comparação de objetos e implementamos o método GetHashCode().

Os métodos e operadores de comparação foram implementados na seguinte ordem:
  1. Para comparar duas instâncias de structs, implementamos o método estático PersonStruct.Equals(PersonStruct, PersonStruct). Usaremos esse método como um método de comparação de referência ao implementar outros métodos e operadores. Além disso, pode ser aplicado para comparar instâncias de structs em linguagens que não suportam operadores.
  2. Os operadores PersonStruct.==(PersonStruct, PersonStruct) e PersonStruct.!=(PersonStruct, PersonStruct) também foram implementados. Deve-se notar que um compilador C# tem as seguintes peculiaridades:
  • Você pode comparar com os operadores sobrecarregados T.==(T, T) e T.!=(T, T) no Nullable(Of T)
  • Antes de verificar uma igualdade de valor, um compilador pode verificar se instâncias de structs têm um valor válido. Além disso, um compilador não agrupa instâncias de structs em objetos.
  • Assim, comparar instâncias da estrutura Nullable(Of T) com um valor anulável não tipado leva a chamar os operadores ==(T, T) ou T.!=(T, T), enquanto compara instâncias do Nullable( De T) sem operadores sobrecarregados T.==(T, T) e T.!=(T, T) resulta na chamada dos operadores Object.==(Object, Object) ou Object.!=(Object, Object) e, como resultado, no encapsulamento de uma instância no objeto.
  1. O método PersonStruct.Equals(PersonStruct) (implementação de IEquatable(Of PersonStruct)) foi implementado chamando o método PersonStruct.Equals(PersonStruct, PersonStruct).
  2. Para evitar envolver instâncias de structs em objetos, quando temos uma ou duas instâncias Nullable(Of PersonStruct), é possível implementar os seguintes métodos:
  • PersonStruct.Equals(PersonStruct?, PersonStruct?), como uma chamada do operador PersonStruct.==(PersonStruct, PersonStruct), é usado para evitar envolver instâncias de structs de ambos os argumentos em objetos e chamar o Object.Equals( Object, Object) se pelo menos um dos argumentos for uma instância Nullable(Of PersonStruct). Além disso, você pode usar esse método para comparar instâncias Nullable(Of PersonStruct) em idiomas que não oferecem suporte a operadores. No código, você pode encontrar comentários explicando como esse método pode ser implementado se um compilador C# não puder usar os operadores T.==(T, T) e T.!=(T, T) para o Nullable(Of T) argumentos.
  • PersonStruct.Equals(PersonStruct?) – a implementação da interface IEquatable(Of PersonStruct?) usada para evitar envolver os argumentos Nullable(Of PersonStruct) em objetos e chamar o método PersonStruct.Equals(Object). Ele é implementado como uma chamada do operador PersonStruct.==(PersonStruct, PersonStruct) com o código comentado para usar os operadores T.==(T, T) e T.!=(T, T) para o Nullable(Of T) ) argumentos.
  • PersonStruct.Equals(Object) – que substitui o método Object.Equals(Object). Ele é implementado verificando a compatibilidade de um tipo de argumento com um tipo do objeto atual usando o operador is lançando o argumento para PersonStruct e chamando PersonStruct.Equals(PersonStruct, PersonStruct).

Notas:
  • A implementação da interface IEquatable(Of PersonStruct?) — IEquatable(Of Nullable(Of PersonStruct)) serve para mostrar problemas específicos na plataforma ao trabalhar com structs em que o encapsulamento de instâncias em objetos acontece mais rápido do que o esperado.
  • >
  • Em projetos reais, desde que não seja necessário melhorar o desempenho, a implementação de IEquatable(Of Nullable(Of T)) não é aplicável por motivos de arquitetura – não devemos implementar IEquatable tipado no tipo T para nenhum tipo.
  • Em geral, não é necessário sobrecarregar um código com otimizações diferentes.

Para estruturas, poderíamos fazer com que a comparação por valor fosse muito mais simples e produtiva, evitando a herança de estruturas definidas pelo usuário e a necessidade de verificar objetos em null. Além disso, podemos monitorar uma nova lógica que suporta argumentos Nullable(Of T).

Em minha futura publicação, resumirei os seguintes pontos:
  • Quando é uma boa ideia implementar a comparação de objetos por valor;
  • Como podemos simplificar a implementação da comparação por valor para objetos – instâncias de classe que representam tipos de referência.

Leia também:


Comparando objetos por valor. Parte 1:Começo

Comparando objetos por valor. Parte 2:Notas de Implementação do Método Equals

Comparando objetos por valor. Parte 3:Igualdades e Operadores de Igualdade Específicos do Tipo

Comparando objetos por valor. Parte 4:Operadores de Herança e Comparação

Comparando objetos por valor. Parte 5:Questão de Igualdade de Estrutura