比较装箱值类型
今天我偶然发现了我写的一个有趣的错误。我有一组可以通过通用设置器设置的属性。这些属性可以是值类型或引用类型。
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()
。进行检查盒装价值观似乎有点矫枉过正。难道就没有更简单的方法吗?
Today I stumbled upon an interesting bug I wrote. I have a set of properties which can be set through a general setter. These properties can be value types or reference types.
public void SetValue( TEnum property, object value )
{
if ( _properties[ property ] != value )
{
// Only come here when the new value is different.
}
}
When writing a unit test for this method I found out the condition is always true for value types. It didn't take me long to figure out this is due to boxing/unboxing. It didn't take me long either to adjust the code to the following:
public void SetValue( TEnum property, object value )
{
if ( !_properties[ property ].Equals( value ) )
{
// Only come here when the new value is different.
}
}
The thing is I'm not entirely satisfied with this solution. I'd like to keep a simple reference comparison, unless the value is boxed.
The current solution I am thinking of is only calling Equals()
for boxed values. Doing a check for a boxed values seems a bit overkill. Isn't there an easier way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果您在处理值类型时需要不同的行为,那么您显然需要执行某种测试。您不需要显式检查装箱值类型,因为由于参数类型为
object
,所以所有值类型都将被装箱**。此代码应满足您规定的条件:如果
value
是(装箱的)值类型,则调用多态Equals
方法,否则使用==
测试参考相等性。( ** 而且,是的,我知道
Nullable
是一种值类型,具有与装箱和拆箱相关的特殊规则,但这在这里几乎无关紧要。)If you need different behaviour when you're dealing with a value-type then you're obviously going to need to perform some kind of test. You don't need an explicit check for boxed value-types, since all value-types will be boxed** due to the parameter being typed as
object
.This code should meet your stated criteria: If
value
is a (boxed) value-type then call the polymorphicEquals
method, otherwise use==
to test for reference equality.( ** And, yes, I know that
Nullable<T>
is a value-type with its own special rules relating to boxing and unboxing, but that's pretty much irrelevant here.)Equals() 通常是首选方法。
.Equals() 的默认实现对引用类型进行简单的引用比较,因此在大多数情况下,这就是您将得到的结果。 Equals() 可能已被重写以提供某些其他行为,但如果有人在类中重写了 .Equals() ,那是因为他们想要更改该类型的相等语义,如果您不这样做,最好让这种情况发生有一个令人信服的理由不这样做。当你的类认为两件事不同,而其他类都同意它们相同时,使用 == 绕过它可能会导致混乱。
Equals() is generally the preferred approach.
The default implementation of .Equals() does a simple reference comparison for reference types, so in most cases that's what you'll be getting. Equals() might have been overridden to provide some other behavior, but if someone has overridden .Equals() in a class it's because they want to change the equality semantics for that type, and it's better to let that happen if you don't have a compelling reason not to. Bypassing it by using == can lead to confusion when your class sees two things as different when every other class agrees that they're the same.
由于输入参数的类型是
object
,因此您将始终在方法的上下文中获得一个装箱值。我认为你唯一的机会是更改方法的签名并编写不同的重载。
Since the input parameter's type is
object
, you will always get a boxed value inside the method's context.I think your only chance is to change the method's signature and to write different overloads.
怎么样:
更新
我意识到这在某些情况下不能按预期工作:
ReferenceEquals
返回 false,因此我们回退到Equals< /code>,其行为符合预期。
ReferenceEquals
返回 true 的引用类型,我们认为它们与预期“相同”。ReferenceEquals
返回 false 和Equals
返回 false 的引用类型,我们认为它们与预期的“不同”。ReferenceEquals
返回 false 和Equals
返回 true 的引用类型,即使我们想要“不同”,我们也会认为它们“相同”,所以教训是“不要自作聪明”
How about this:
Update
I realized this doesn't work as expected for a certain case:
ReferenceEquals
returns false so we fall back toEquals
, which behaves as expected.ReferenceEquals
returns true, we consider them "same" as expected.ReferenceEquals
returns false andEquals
returns false, we consider them "different" as expected.ReferenceEquals
returns false andEquals
returns true, we consider them "same" even though we want "different"So the lesson is "don't get clever"
我想
有点相当于
这意味着您需要做的第一件事是检查该值是否已装箱。
如果存在一种方法来检查对象是否是装箱值类型,那么它应该至少与您提供链接的“overkill”方法一样复杂,除非这不是最简单的方法。尽管如此,应该有一种“最简单的方法”来确定对象是否是装箱值类型。这种“最简单的方法”不太可能比简单地使用对象 Equals() 方法更简单,但我已经为这个问题添加了书签,以防万一。
(不确定我是否符合逻辑)
I suppose
is somewhat equivalent to
This means the first thing you'll need to do is to check whether the value is boxed or not.
If there exists a method to check whether an object is a boxed value type or not, it should be at least as complex as that "overkill" method you provided the link to unless that is not the simplest way. Nonetheless, there should be a "simplest way" to determine if an object is a boxed value type or not. It's unlikely that this "simplest way" is simpler than simply using the object Equals() method, but I've bookmarked this question to find out just in case.
(not sure if I was logical)