比较盒装值类型

时间:2011-06-01 17:06:59

标签: c# equality boxing unboxing

今天我偶然发现了一个有趣的错误。我有一组属性可以通过一般的setter设置。这些属性可以是值类型或引用类型。

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

为此方法编写单元测试时,我发现值类型的条件始终为真。我花了很长时间才弄清楚这是由于boxing/unboxing。我花了很长时间才将代码调整为以下内容:

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

问题是我对这个解决方案并不完全满意。我想保留一个简单的参考比较,除非该值被装箱。

我想到的当前解决方案只是为盒装值调用Equals()。做a check for a boxed values似乎有点矫枉过正。是不是有更简单的方法?

5 个答案:

答案 0 :(得分:18)

如果您在处理值类型时需要不同的行为,那么您显然需要执行某种测试。您不需要显式检查盒装值类型,因为所有值类型都将被装箱**,因为参数被输入为object

此代码应符合您声明的标准:如果value是(盒装)值类型,则调用多态Equals方法,否则使用==测试引用相等。< / p>

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>是一个值类型,它有自己的与装箱和拆箱有关的特殊规则,但这在这里几乎无关紧要。)

答案 1 :(得分:10)

Equals()通常是首选方法。

.Equals()的默认实现对引用类型进行简单的引用比较,因此在大多数情况下,这就是您将获得的内容。可能已经重写了Equals()以提供其他一些行为,但如果有人在类中重写了.Equals(),那是因为他们想要更改该类型的相等语义,如果不这样做,最好让它发生。有一个令人信服的理由不这样做。使用==绕过它可能会导致混乱,当你的班级认为两件事情不同时,其他每个班级都同意他们是相同的。

答案 2 :(得分:1)

由于输入参数的类型为object,因此您将始终在方法的上下文中获得一个盒装值。

我认为您唯一的机会是更改方法的签名并编写不同的重载。

答案 3 :(得分:1)

这个怎么样:

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的参考类型,我们认为它们“相同”,即使我们想要“不同”

所以教训是“不要聪明”

答案 4 :(得分:0)

我想

  

我想保留一个简单的参考比较,除非该值被装箱。

有点等同于

  

如果该值已装箱,我将进行非“简单参考比较”。

这意味着您需要做的第一件事就是检查值是否已装箱。

如果存在检查对象是否为盒装值类型的方法,则它应该至少与您提供链接的“overkill”方法一样复杂,除非这不是最简单的方法。尽管如此,应该有一种“最简单的方法”来确定对象是否是盒装值类型。这种“最简单的方法”不太可能比简单地使用对象Equals()方法简单,但我已经将这个问题加入书签以便以防万一。

(不确定我是否符合逻辑)