【问题标题】:Comparing structs for equality without boxing在没有装箱的情况下比较结构是否相等
【发布时间】:2016-08-17 18:17:42
【问题描述】:

我遇到了一个扩展方法,它适用于结构 (SomeStruct) 并返回值是否等于 default(SomeStruct)(当调用无参数构造函数时)。

public static bool IsDefault<T> (this T value)
    where T : struct
{
    return (!EqualityComparer<T>.Default.Equals(value, default(T)));
}

这让我想知道结构是否被装箱。这纯粹是出于好奇,因为拳击/按值传递有优缺点,具体取决于上下文。

假设:

  1. 以下第一个方法是非法的,因为结构不会隐式覆盖相等运算符 ==/!=
  2. 第二个“出现”是为了避免拳击。
  3. 第三种方法应该始终将结构装箱,因为它正在调用object.Equals(object o)
  4. 第四个有两个重载可用(object/T) 所以我假设它也会避免装箱。但是,目标结构需要实现IEquatable&lt;T&gt; 接口,这使得辅助扩展方法不是很有帮助。

变化:

public static bool IsDefault<T> (this T value)
    where T : struct
{
    // Illegal since there is no way to know whether T implements the ==/!= operators.
    return (value == default(T));
}

public static bool IsDefault<T> (this T value)
    where T : struct
{
    return (!EqualityComparer<T>.Default.Equals(value, default(T)));
}

public static bool IsDefault<T> (this T value)
    where T : struct
{
    return (value.Equals(default(T)));
}

public static bool IsDefault<T> (this T value)
    where T : struct, IEquatable<T>
{
    return (value.Equals(default(T)));
}

这个问题是关于确认上述假设以及我是否误解和/或遗漏了什么。

【问题讨论】:

    标签: c# .net equality value-type boxing


    【解决方案1】:
    1. 以下第一个方法是非法的,因为结构不会隐式覆盖相等运算符 ==/!=。

    是的。

    1. 第二个“出现”是为了避免拳击。

    被调用方法的签名是EqualityComparer&lt;T&gt;.Equals(T,T),它使用T类型作为参数,所以调用时不需要装箱。

    默认比较器的实现检查T 是否为IEquatable&lt;T&gt;,如果是,则使用使用IEquatable&lt;T&gt;.Equals 的比较器,否则使用Object.Equals 的比较器,因此如果结构是内部可能会应用装箱不是IEquatable('仅在需要时')。

    1. 第三种方法应该始终将结构装箱,因为它正在调用 object.Equals(object o)。

    是的。

    1. 第四个有两个可用的重载(对象/T),所以我假设它也会避免装箱。但是,目标结构需要实现 IEquatable 接口,这使得辅助扩展方法不是很有帮助。

    是的,根据this SO answer,它不需要拳击。这是您从EqualityComparer&lt;T&gt;.Default 获得的针对T : IEquatable 特定情况的有效代码。

    【讨论】:

    • 谢谢。所以我知道第二种情况如果不实现 IEquatable 将需要装箱?如果是这样,没有消费者(结构的创建者)实现接口(为了使用我们的库而做更多的工作),没有一种方法是未装箱的操作。
    • 装箱成本非常低,尤其是与典型的对象比较相比,因此实际上我不会担心它。如果结构的创建者没有实现相等,则它遵循Object.Equals 给出的默认相等,因此装箱是不可避免的,但与(字节比较/反射)成本相比也不会产生影响Object.Equals.
    • 性能和成本是非常主观的。我遇到过拳击成本远远超过auto public properties 的情况。就我而言,我必须为 Vector2 结构实现 public readonly 字段,这对带有支持字段的属性带来了可笑的性能改进。但这些是专门的场景,开发人员已经知道足够的知识来提供相关的内置比较。除此之外,您能否评论一下:“如果不实现 IEquatable,第二种情况将需要装箱吗?”。
    • 是的,它会的。但是,如果该结构没有实现 IEquateable,则没有装箱就无法测试公平性。
    • @RaheelKhan 就像 Scott 所说,如果结构没有实现 IEquatable,那么你唯一的选择就是调用 Object.Equals(直接或间接),所以拳击将会发生,你无能为力去做吧。
    【解决方案2】:

    让我补充一下structs,如果你不定义比较,细节会变得复杂。

    例如How to define value equality for a type 说:

    您定义的任何结构都已经具有值相等的默认实现,它继承自 Object.Equals(Object) 方法的 System.ValueType 覆盖。此实现使用反射来检查类型中的所有字段和属性。尽管此实现产生了正确的结果,但与您专门为该类型编写的自定义实现相比,它相对较慢。

    更多详情请参见Performance implications of default struct equality in C#,包括:

    Equals 和 GetHashCode 有一个优化的默认版本,但你永远不应该依赖它,因为你可能会停止对它进行无辜的代码更改。

    【讨论】:

      猜你喜欢
      • 2013-04-15
      • 2010-09-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多