今天我偶然发现了一个有趣的错误。我有一组属性可以通过一般的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似乎有点矫枉过正。是不是有更简单的方法?
答案 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()方法简单,但我已经将这个问题加入书签以便以防万一。
(不确定我是否符合逻辑)