【问题标题】:Operator '==' can't be applied to type T?运算符 '==' 不能应用于类型 T?
【发布时间】:2011-08-14 01:31:39
【问题描述】:

我认为这个方法是有效的但我错了:

static void Equals<T>(T x, T y)
{
    return x == y;    //operator == can't be applied to type T
}

阅读规范后(v3.0 中的第 7.2.4 节和 v4.0 中的第 7.3.4 节):

7.2.4 二元运算符重载解析

x 形式的运算 op y,其中 op 是可重载的 二元运算符,x 是 类型 X,y 是类型的表达式 Y,处理如下:

  • 候选用户定义运算符集 由 X 和 Y 提供用于操作 确定算子 op(x, y)。这 集合由 X 提供的候选运算符和 Y提供的候选运算符, 每个使用的规则确定 §7.2.5。如果 X 和 Y 是同一类型, 或者如果 X 和 Y 来自于 公共基础类型,然后共享 候选运算符仅出现在 合集一次。

  • 如果集合 候选用户定义的运算符是 不为空,则这成为集合 的候选运营商 手术。否则,预定义的 二元运算符 op 实现, 包括他们的提升形式,成为 的候选运算符集 手术。预定义的 给定运算符的实现 在描述中指定 运算符(第 7.7 节到第 7.11 节)。

  • 第 7.4.3 节的重载决策规则应用于候选运算符集,以选择关于参数列表 (x, y) 的最佳运算符,该运算符成为重载决策的结果过程。如果重载决策未能选择单个最佳运算符,则会发生编译时错误。

在第 2 步中,我认为应该应用这个预定义的实现:

bool operator ==(object x, object y);
bool operator !=(object x, object y);

因为 C# 中的所有内容都派生自 Object。步骤 3 中如何发生编译时错误?在这种情况下,我认为不可能出现“重载分辨率无法选择”。

编辑当我实施这样的事情时,我想到了这个问题:

class EnumComparer<TEnum> : IEqualityComparer<TEnum>
{
    public bool Equals(TEnum x, TEnum y)
    {
        return x == y;
    }
    public int GetHashCode(TEnum obj)
    {
        return (int)obj;
    }
}

恐怕我需要构建一个表达式并在Equals方法中动态调用它。

【问题讨论】:

  • FWIW, (object)x == (object)y 是有效的——但考虑 (object)1 == (object)1 作为不希望这样做的原因。只需要帮助T 到非泛型。
  • 讨论有点抽象,但 Eric Lippert 指出,所有内容派生都来自Object 是一种普遍的误解。 blogs.msdn.com/b/ericlippert/archive/2009/08/06/… 他提到的一个相关点是类型参数不是从任何东西派生的。
  • @pst:我认为(dynamic)x == (dynamic)y 是您想要的。否则它只是做一个引用相等。
  • 我有点困惑;为什么在枚举上内置的平等实现还不够?也就是说,为什么不直接使用默认的相等比较器呢?

标签: c# .net operator-overloading language-specifications


【解决方案1】:

使用.Equals() 方法并确保T 实现IComparable

【讨论】:

  • IComparable[&lt;T&gt;]排序;等于(GetHashCose() 和可选的IEquatable&lt;T&gt;)是相等的。
【解决方案2】:

如果它知道where T : class 进行参考比较,这可能会起作用。操作员通常很少支持泛型,但有一些变通方法。 MiscUtil 在泛型上提供indirect support for operators,否则EqualityComparer&lt;T&gt;.Default.Equals(x,y) 是一个不错的选择。

【讨论】:

  • 谢谢!我只想为枚举实现一个比较器。查看我的编辑。
  • @Danny - 然后尝试EqualityComparer&lt;T&gt;.Default.Equals(x,y)
  • 大声笑!我走错了方向!非常感谢您的建议!
  • 嗨 Marc,EqualityComparer.Default.Equals(x,y) 的实现看起来像 x == yx.Equals(y)?(源代码没有告诉我)。后者会引起拳击吗?
  • @Danny 如果实现了IEquatable&lt;T&gt;,则没有装箱 - 但我刚刚检查过,令人讨厌的是枚举并非如此。呸!如果你真的想要的话,我可以写一个通过元编程工作的(一些 ILGenerator 操作)
【解决方案3】:

阅读规范对您很有帮助,但您过早停止阅读。如果您进一步阅读,您将了解这一点:


预定义的引用类型相等运算符需要以下之一:

  • 两个操作数都是已知为引用类型或文字 null 的类型的值。此外,存在从任一操作数类型到另一操作数类型的显式引用转换。

  • 一个操作数是 T 类型的值,其中 T 是类型参数,另一个操作数是字面量 null。此外,T 没有值类型约束。

除非这些条件之一为真,否则会发生绑定时错误。 (*)


错误不是来自重载决议;错误在于重载解析会选择预定义的引用类型相等运算符,而您没有引用类型。

考虑您的代码。是什么阻止了 T 成为一个没有定义相等运算符的值类型?没有。假设我们退回到对象版本;两个操作数都会装箱到不同的位置,因此即使它们具有相同的内容,它们也是不相等的。由于这很慢、令人困惑和错误,因此尝试也是违法的。

你为什么首先尝试做这件事?如果您的方法有效,但它没有,那么您的方法将比首先使用 == 更更糟糕。您打算通过这种方法为世界增加什么价值?


(*) 我已经向规范维护者报告了这句话中的语法错误。

【讨论】:

【解决方案4】:

我喜欢为此使用EqualityComparer&lt;T&gt;.Default

它基于重写的Equals 方法,但在可用时使用IEquatable&lt;T&gt;,避免对实现它的值类型进行装箱。

EqualityComparer<T>.Default.Equals(x, y)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-04
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-09
    相关资源
    最近更新 更多