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

Сравнение типов полей в штучной упаковке

Сегодня я наткнулся на интересную ошибку, которую я написал. У меня есть набор свойств, которые можно установить через общий сеттер. Эти свойства могут быть типами значений или ссылочными типами.

public void SetValue( TEnum property, object value )
{
    if ( _properties[ property ] != value )
    {
        // Only come here when the new value is different.
    }
}

При написании модульного теста для этого метода я выяснил, что условие всегда верно для типов значений. Мне не потребовалось много времени, чтобы понять, что это связано с боксом/распаковкой. Мне не потребовалось много времени, чтобы настроить код на следующее:

public void SetValue( TEnum property, object value )
{
    if ( !_properties[ property ].Equals( value ) )
    {
        // Only come here when the new value is different.
    }
}

Дело в том, что я не полностью удовлетворен этим решением. Я бы хотел провести простое сравнение ссылок, если значение не помещено в коробку.

Текущее решение, о котором я думаю, вызывает только Equals() для значений в штучной упаковке. Выполнение проверки для значений в штучной упаковке кажется немного излишним. Разве нет более простого пути?

4b9b3361

Ответ 1

Если вам нужно другое поведение, когда вы имеете дело с типом значения, вам, очевидно, потребуется выполнить какой-то тест. Вам не нужна явная проверка для вложенных значений типов значений, так как все типы значений будут вставляться в бокс ** из-за параметра, набираемого как object.

Этот код должен соответствовать вашим заявленным критериям: Если value является (boxed) значением типа, тогда вызовите полиморфный метод Equals, в противном случае используйте == для проверки ссылочного равенства.

public void SetValue(TEnum property, object value)
{
    bool equal = ((value != null) && value.GetType().IsValueType)
                     ? value.Equals(_properties[property])
                     : (value == _properties[property]);

    if (!equal)
    {
        // Only come here when the new value is different.
    }
}

(** И, да, я знаю, что Nullable<T> - это тип значения со своими собственными специальными правилами, касающимися бокса и распаковки, но это почти не имеет значения здесь.)

Ответ 2

Равен(), как правило, является предпочтительным подходом.

Реализация по умолчанию .Equals() делает простое сравнение ссылок для ссылочных типов, поэтому в большинстве случаев это то, что вы получите. Equals() может быть переопределен, чтобы обеспечить какое-то другое поведение, но если кто-то переопределил .Equals() в классе, потому что они хотят изменить семантику равенства для этого типа, и лучше позволить, чтобы это произошло, если вы не есть веская причина не делать этого. Обход этого с помощью == может привести к путанице, когда ваш класс видит две вещи как разные, когда каждый другой класс соглашается, что они одинаковы.

Ответ 3

Так как тип входного параметра object, вы всегда получите значение в коробке внутри контекста метода.

Я думаю, что ваш единственный шанс - изменить подпись метода и написать разные перегрузки.

Ответ 4

Как насчет этого:

if(object.ReferenceEquals(first, second)) { return; }
if(first.Equals(second)) { return; }

// they must differ, right?

Обновление

Я понял, что это не работает в определенном случае:

  • Для типов значений ReferenceEquals возвращает false, поэтому мы возвращаемся к Equals, который ведет себя как ожидалось.
  • Для ссылочных типов, где ReferenceEquals возвращает true, мы считаем их "такими же", как ожидалось.
  • Для ссылочных типов, где ReferenceEquals возвращает false и Equals возвращает false, мы считаем их "разными", как ожидалось.
  • Для ссылочных типов, где ReferenceEquals возвращает false, а Equals возвращает true, мы считаем их "одинаковыми", хотя мы хотим "разные"

Итак, урок "не получается умным"

Ответ 5

Предположим, что

Я хотел бы сохранить простое сравнение ссылок, если значение не помещено в коробку.

несколько эквивалентен

Если значение помечено в квадрат, я сделаю "простое сравнение ссылок".

Это означает, что первое, что вам нужно сделать, это проверить, включено ли значение в коробке или нет.

Если существует метод проверки того, является ли объект типом с коротким значением или нет, он должен быть, по крайней мере, таким же сложным, как этот метод "overkill", который вы предоставили ссылку, если это не самый простой способ. Тем не менее, должен быть "самый простой способ" определить, является ли объект типом с коротким значением или нет. Маловероятно, что этот "самый простой способ" проще, чем просто использовать метод Equals(), но я задал этот вопрос, чтобы узнать на всякий случай.

(не уверен, что я был логичен)