Сравнение типов в штучной упаковке - PullRequest
33 голосов
/ 01 июня 2011

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

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() только для значений в штучной упаковке.Выполнение проверки значений в штучной упаковке кажется немного излишним.Есть ли более простой способ?

Ответы [ 5 ]

19 голосов
/ 02 июня 2011

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

Этот код должен соответствовать указанным вами критериям: если value является типом значения (в штучной упаковке), тогда вызовите полиморфный метод 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> является типом значения со своими собственными специальными правилами, касающимися упаковки и распаковки, но здесь это в значительной степени не имеет значения.)

10 голосов
/ 01 июня 2011

Equals () обычно является предпочтительным подходом.

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

1 голос
/ 01 июня 2011

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

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, мы считаем их «одинаковыми», хотя мы хотим «отличаться»

Таким образом, урок «не становись умным»

1 голос
/ 01 июня 2011

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

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

0 голосов
/ 01 июня 2011

Полагаю,

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

в некоторой степени эквивалентно

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

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

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

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

...