Подтвердить что ты не робот

Dictionary.ContainsKey() - Как это работает?

Я прочитал документацию MSDN о том, как работает Dictionary.ContainsKey(), но мне было интересно, как это фактически делает сравнение равенства? В принципе, у меня есть словарь, привязанный к ссылочному типу *, и я хочу, чтобы метод ContainsKey() проверял определенное свойство этого ссылочного типа в качестве основы для определения наличия ключа или нет. Например, если у меня есть Dictionary(MyObject, int) и MyObject имеет общедоступное свойство (из int), называемое "TypeID", могу ли я получить ContainsKey(MyObject myObject), чтобы проверить, имеет ли один из ключей TypeID, который равна MyObject? Могу ли я просто перегрузить оператор ==?

  • Тип ссылки - это объект с названием "Длительность", который содержит значение (double Length); "Длительность" - это базовый тип, используемый в моей музыкальной программе, для обозначения того, как долго длится определенный звук. Я извлекаю из него классы, которые включают более сложные временные концепции, такие как западные музыкальные сигнатуры времени, но хотят, чтобы все они были сопоставимыми по длине.

EDIT: Как было предложено, я реализовал IEquitable на моем объекте следующим образом:

 public class Duration : IEquatable<Duration>
 {
    protected double _length;

    /// <summary>
    /// Gets or Sets the duration in Miliseconds.
    /// </summary>
    public virtual double Length
{
        get
        {
            return _length;
        }
        set
        {
            _length = value;
        }
    }

// removed all the other code that as it was irrelevant

    public override bool Equals(object obj)
    {
        Duration otherDuration = (Duration)obj;
        if (otherDuration._length == _length)
        {
            return true;
        }
        else
        {
            return false
        }
    }

}

Это все, что мне нужно сделать?

4b9b3361

Ответ 1

EDIT: вот код для вашего обновленного примера. Примечание. Мне показалось немного странным, что вы открываете поле как защищенное, а также имеете виртуальное свойство, которое предоставляет член. Под этой схемой что-то может переопределить Length, что приведет к равенству, которое смотрит на _lenght, чтобы не вести себя так, как ожидалось.

public class Duration : IEquatable<Duration>
{
    protected double _length;

    /// <summary>
    /// Gets or Sets the duration in Miliseconds.
    /// </summary>
    public virtual double Length
    {
        get { return _length; }
        set { _length = value; }
    }

    // removed all the other code that as it was irrelevant

    public bool Equals(Duration other)
    {
        // First two lines are just optimizations
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;

        return _length.Equals(other._length);
    }

    public override bool Equals(object obj)
    {
        // Again just optimization
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;

        // Actually check the type, should not throw exception from Equals override
        if (obj.GetType() != this.GetType()) return false;

        // Call the implementation from IEquatable
        return Equals((Duration) obj);
    }

    public override int GetHashCode()
    {
        // Constant because equals tests mutable member.
        // This will give poor hash performance, but will prevent bugs.
        return 0;
    }
}

См. EqualityComparer.Default для информации о стандартном IEqualityComparer, используемом классом Dictionary.

Если вы не хотите вообще переопределять GetHashCode и Equals в классе, или если вы не можете. Существует перегрузка конструктора Dictionary , в которой вы можете указать конкретный IEqualityComparer для использования.

Это простой интерфейс для реализации, но вам нужно быть осторожным, чтобы вы соблюдали контракт для GetHashCode, или вы можете оказаться в неожиданном поведении.

public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
    public bool Equals(MyObject x, MyObject y)
    {
        return x.TypeID == y.TypeID;
    }

    public int GetHashCode(MyObject obj)
    {
        return obj.TypeID; //Already an int
    }
}

использовать его просто go

new Dictionary<MyObject, int>(new MyObjectEqualityComparer());   

Если вы хотите использовать IEqualityComparer по умолчанию, вам необходимо предоставить примерно одинаковые методы в MyObjectEqualityComparer. Вы можете избегать переопределения object.Equals(), если вы реализуете IEquatable. Однако я бы сильно отговорил его, потому что это может привести к неожиданному поведению. Вам лучше переопределить Equals, чтобы у вас было последовательное поведение для всех вызовов Equals и иметь хеширование, которое правильно соответствует Equals. Мне пришлось исправить ошибку в унаследованном коде, вызванную прошлым разработчиком, реализующим только IEquatable.

Ответ 2

Внутри Dictionary используется EqualityComparer. Во-первых, он проверяет, реализует ли ключ IEquatable. Если ключ не реализует этот интерфейс, он вызывает метод Equals.