Перегрузка == оператор для класса, содержащего только строковые атрибуты

Каков наилучший (самый элегантный или эффективный) способ перегрузки оператора равенства в классе, содержащем только строковые атрибуты?

Пример:

class MagicClass
{
    public string FirstAttribute { get; set; }
    public string SecondAttribute { get; set; }
    public string ThirdAttribute { get; set; }
    public string FourthAttribute { get; set; }
    public string FifthAttribute { get; set; }
}

Я знаю, как перегрузить самого оператора, однако меня интересуют следующие моменты:

  1. Есть ли способ элегантно сравнить такие два объекта (например, без необходимости писать if утверждение, содержащее взаимные сравнения всех атрибутов
  2. Что было бы хорошей реализацией GetHashCode() метод в таком случае

2 ответа

Решение

Как насчет этого, Просто создайте массив всех свойств и цикл.

internal class MagicClass
{
    public string FirstAttribute { get; set; }
    public string SecondAttribute { get; set; }
    public string ThirdAttribute { get; set; }
    public string FourthAttribute { get; set; }
    public string FifthAttribute { get; set; }

    private string[] AllProperties//Array of all properties
    {
        get
        {
            return new[]
            {
                FirstAttribute,
                SecondAttribute,
                ThirdAttribute,
                FourthAttribute,
                FifthAttribute
            };
        }
    }

    protected bool Equals(MagicClass other)
    {
        var thisProps = this.AllProperties;
        var otherProps = other.AllProperties;

        return thisProps.SequenceEqual(otherProps);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((MagicClass) obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            var thisProps = this.AllProperties;
            int hashCode = 0;
            foreach (var prop in thisProps)
            {
                hashCode = (hashCode * 397) ^ (prop != null ? prop.GetHashCode() : 0);
            }
            return hashCode;
        }
    }
}

Тогда вы можете позвонить Equals метод внутри вашего оператора перегрузки. Если вам лень создавать AllProperties массив вы можете использовать Reflection но отражение ИМО здесь излишне.

Не говоря уже о том, что это "лучшее" или самое элегантное решение, но я бы имел тенденцию использовать массив и инициализатор индекса, используя перечисление, так что я мог бы повторно использовать get и установить логику и в этом случае сбросить хеш-код для быстрого первого сравнения. Преимущество перечисления состоит в том, что вам не нужно перепроверять логику сравнения при добавлении атрибута, и вы можете избежать дополнительных затрат на рефлексию.

class MagicClass
{
    string[] Values = new string[Enum.GetValues(typeof(MagicClassValues)).Length];

    public string this[MagicClassValues Value] //and/or a GetValue/SetValue construction
    {
        get
        {
            return Values[(int)Value];
        }
        set
        {
            Values[(int)Value] = value;
            hash = null;
        }
    }

    int? hash; //buffered for optimal dictionary performance and == comparisson
    public override int GetHashCode()
    {
        if (hash == null)
            unchecked
            {
                hash = Values.Sum(s => s.GetHashCode());
            }
        return hash.Value;
    }

    public static bool operator ==(MagicClass v1, MagicClass v2) //used == operator, in compliance to the question, but this would be better for 'Equals'
    {
        if(ReferenceEquals(v1,v2))return true;
        if(ReferenceEquals(v1,null) || ReferenceEquals(v2,null) || v1.GetHashCode() != v2.GetHashCode())return false;
        return v1.Values.SequenceEqual(v2.Values);
    }
    public static bool operator !=(MagicClass v1, MagicClass v2)
    {
        return !(v1 == v2);
    }

    //optional, use hard named properties as well
    public string FirstAttribute { get { return this[MagicClassValues.FirstAttribute]; } set { this[MagicClassValues.FirstAttribute] = value; } }
}

public enum MagicClassValues
{
    FirstAttribute,
    SecondAttribute,
    //etc
}
Другие вопросы по тегам