【问题标题】:Operator overloading and different types运算符重载和不同类型
【发布时间】:2011-08-14 03:51:56
【问题描述】:

我有一个班级分数,它将在与整数的比较中大量使用。我打算按照下面的代码重载 == 运算符以启用这些比较?

public class Score
{
    public Score(int score) {
        Value = score;
    }

    public static bool operator ==(Score x, int y) {
        return x != null && x.Value == y;
    }

    public static bool operator ==(int y, Score x)
    {
        return x != null && x.Value == y;
    }
}

这是对运算符重载的合理使用吗?

我是否应该为运算符的 LH 和 RH 侧提供重载以允许使用对称?

【问题讨论】:

    标签: c#


    【解决方案1】:

    我可能会继续定义从intScore 的隐式转换,这样当你处理相等时,你只需要处理一个类型。

    public static implicit operator Score(int value)
    {
        return new Score { Value = value }; // or new Score(value);
    }
    // define bool operator ==(Score score1, Score score2)
    
    // elsewhere 
    Score score = new Score { Value = 1 };
    bool isScoreOne = (score == 1);
    

    当您定义自己的== 运算符时,请记住继续定义!=,并覆盖EqualsGetHashCode

    【讨论】:

    • 我刚试过这个,但它没有编译 -- 不能将运算符 '==' 应用于 'int' 和 'Score' 类型的操作数
    • 没关系,当我摆脱其他重载时,我注释掉了我的 class== 运算符。
    • 看了我的场景后,这实际上是我所追求的。对象可能比较简单,但会被广泛使用。
    【解决方案2】:

    我确实认为使用运算符重载是一种奇怪的情况,但这是你的决定。

    但是,我的主要观点是,如果您重载 ==,您也将被要求重载!=

    如果您随后重载 !=,则使用 x != null 比较 x 以检查它是否不为空的部分将导致 == 运算符调用 != 运算符。这本身不是问题,只要不使用 == 比较,因为您将有一组递归调用,从而导致堆栈溢出。

    但是,由于很多人在重载 != 时将其实现为 'not ==' - 在您的情况下,这将导致堆栈溢出。

    解决方案:特别是在重载 ==、!= 和 Equals() 时,最好使用 Object.ReferenceEquals(x, null); 与 null 进行比较。

    【讨论】:

      【解决方案3】:

      通常当我看到一个重载的运算符时,它会将该类与自身进行比较。因此,如果您有多个实例变量,您将弄清楚如何比较两者以确定某些东西是否相等、更大、更少等。

      我不明白你为什么不这样做:

      if(myScore.value == 5)
      {
       //do stuff
      }
      

      【讨论】:

      • +1 同意。在这种情况中,重载似乎并没有“增加价值”。
      • 在我看来,公开 .value 的实际实现是一个坏主意。如果代码更改为将分数测量为双精度值,但比较应该是四舍五入的值怎么办?如果他一开始就使用运算符重载,您就可以在不更改用法的情况下更改实现。
      • 隐藏 Value 的实现是一个目标。 @pst 在某种程度上我希望减少代码混乱。 if(myScore == 5) 比 if(myScore.Value == 5) 更具可读性.. 这是一件小事,但需要考虑
      • @thelsdj 如果价值合同的一部分,则没有变化。有人可以争辩说“如果它从 int 更改为 float 会怎样”——我认为没有区别。
      【解决方案4】:

      “这是对运算符重载的合理使用吗?”

      如果它使您的代码更易于理解并且不会引起问题,我会说,是的,绝对是!可能还有其他方法,但如果这对您有用,我认为没有问题。

      “我应该为运算符的 LH 和 RH 侧提供重载以允许使用对称吗?”

      我会争辩说,除非你有特定的理由这样做,换句话说,如果你正在使用或需要它们,那么你就不需要它。 (YAGNI) 我看到的唯一真正的例外是,如果您正在编写一个框架,并且您有一个很好的想法,其他人会需要它。

      【讨论】:

        【解决方案5】:

        tl;dr不,这不是一个明智的想法。

        1. 人们觉得有必要提出或搜索这个问题的事实已经说明了这一点。
        2. 似乎很少有人关心这一事实表明,可能有一种方法可以简单地避免您需要首先提出问题的情况。
        3. 最后两点至少应该迫使您重新考虑一些事情,无论它们可能似乎是无关的。

        考虑尽快在域逻辑之外为软件组件的所有输入分配类型。这样一来,我们通常所说的“领域模型”就不用担心这个了。

        在您的情况下,这意味着将所有分数包装在 Score 对象中。因为他们就是这样。产生异常的需要总是至少令人怀疑,并且有资格作为代码异味。

        下一步是将您的“领域模型”视为没什么特别的——只是另一组模块。将所有使用有形原语的(通常令人讨厌的)I/O 接口视为履行不同/单独的角色。这自然会(并且几乎神奇地)降低所有组件(例如类)的单个复杂性。

        这样做的一个副作用是,您可能会发现这个问题变得荒谬。 “如果我什至不确定整数是分数,为什么我需要将分数与原始整数进行比较?如果我确定,那么为什么我之前不这么说(例如通过装箱)? "

        更进一步,您可能会意识到您的许多算法甚至不需要知道分数是如何保持的:很可能它们中的大多数只需要能够比较它们以查看它们是否相等或等于看看哪个更高。我并不是说修改如何你保持分数的灵活性在未来会有用,但作为规则如果它不重要那么没有必要打成平手代码实现只是为了可爱。也许令人惊讶的是,这也适用于诸如整数之类的原语,甚至是数字的概念(在所有需要比较两个分数的情况下,您都不需要关心它)。 SOLID 原则提供了一个很好的指导,直到您可以为自己培养直觉。

        反驳:人们总是可以用性能论据来反对这种思路。根据我的经验,我发现 JIT 会优化和内联任何影响性能的东西(尤其是像相等比较这样的琐碎事情),而当它没有时,几乎总是有一种方法可以以理智的方式抽象性能关键代码。如果调度是一个问题,请不要犹豫密封您的类型。

        在测量并找到正确设计中的瓶颈之后,该瓶颈无法通过替代但同样合理的设计来解决,那么人们需要诚实地承认他们遇到了语言限制,在这种情况下,应该考虑提取该代码并用更合适的语言编写它,或者使用现有语言进行修改,但将该代码注释掉。

        最后,我要提一下,我实际上完全按照您过去的建议进行了操作,我只能感谢有问题的代码从未投入生产。做一次,它似乎没有害处,但是到处做,不安的感觉就会安定下来,只会产生挫败感,直到你最终意识到为什么它是错误的(根据上述考虑)。如果您很好奇,我对代表实体键的类型执行了此操作,这些实体键几乎总是包装一个原语。再一次,这似乎是一个明智的想法,在你看来它可能仍然是它,但我坚信现在不是,即使我以前不会听说它。也许这是你需要在它最终沉没之前烧掉自己的东西之一。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-26
          • 1970-01-01
          • 2013-11-21
          • 1970-01-01
          • 2014-06-02
          相关资源
          最近更新 更多