比较装箱值类型

发布于 2024-11-11 17:06:21 字数 887 浏览 6 评论 0原文

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

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

时光倒影 2024-11-18 17:06:21

如果您在处理值类型时需要不同的行为,那么您显然需要执行某种测试。您不需要显式检查装箱值类型,因为由于参数类型为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 是一种值类型,具有与装箱和拆箱相关的特殊规则,但这在这里几乎无关紧要。)

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 polymorphic Equals method, otherwise use == to test for reference equality.

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.
    }
}

( ** 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.)

北恋 2024-11-18 17:06:21

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.

冷情妓 2024-11-18 17:06:21

由于输入参数的类型是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.

梦初启 2024-11-18 17:06:21

怎么样:

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

// they must differ, right?

更新

我意识到这在某些情况下不能按预期工作:

  • 对于值类型,ReferenceEquals返回 false,因此我们回退到Equals< /code>,其行为符合预期。
  • 对于 ReferenceEquals 返回 true 的引用类型,我们认为它们与预期“相同”。
  • 对于 ReferenceEquals 返回 false 和 Equals 返回 false 的引用类型,我们认为它们与预期的“不同”。
  • 对于 ReferenceEquals 返回 false 和 Equals 返回 true 的引用类型,即使我们想要“不同”,我们也会认为它们“相同”,

所以教训是“不要自作聪明”

How about this:

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

// they must differ, right?

Update

I realized this doesn't work as expected for a certain case:

  • For value types, ReferenceEquals returns false so we fall back to Equals, which behaves as expected.
  • For reference types where ReferenceEquals returns true, we consider them "same" as expected.
  • For reference types where ReferenceEquals returns false and Equals returns false, we consider them "different" as expected.
  • For reference types where ReferenceEquals returns false and Equals returns true, we consider them "same" even though we want "different"

So the lesson is "don't get clever"

洛阳烟雨空心柳 2024-11-18 17:06:21

我想

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

有点相当于

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

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

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

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

I suppose

I'd like to keep a simple reference comparison, unless the value is boxed.

is somewhat equivalent to

If the value is boxed, I'll do a non-"simple reference comparison".

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)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文