运算符重载和不同类型

我有一个课程分数,它将大量用于比较整数。 我打算重载==运算符以根据下面的代码启用这些比较?

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侧提供过载以允许使用对称?

我可以继续定义从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

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

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

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

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

但是,我的主要观点是,如果你重载==你也需要超载!=

如果你然后重载!=,你比较x以使用x != null检查它是否x != null将导致,==运算符调用!=运算符。 这本身不是问题,只要这不会使用==比较,因为你将有一个递归的调用集,导致堆栈溢出。

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

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

“这是运算符重载的明智用法吗?”

如果它使您的代码更容易理解并且不会导致问题,我会说,是的,绝对! 可能还有其他方法,但如果这对你有用,我没有看到问题。

“我是否应该为操作员的LH和RH侧提供过载以允许使用对称?”

我认为,除非你有特定的理由这样做,换句话说,如果你正在使用或需要它们,那么你就不会需要它。 (YAGNI)我看到的唯一真正的例外是,如果你正在编写一个框架,你可以很好地了解别人会需要它。

tl; dr在盛大的计划中; 不,这不是一个明智的想法。

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

请考虑尽快在域逻辑之外为软件组件的所有输入分配类型。 通过这种方式,我们通常所说的“域模型”永远不必担心这一点。

在您的情况下,这意味着包装Score对象中的所有分数。 因为那是他们的本意。 制作例外的需要总是至少是可疑的并且有资格作为代码气味。

下一步是将您的“域模型”视为没有什么特别的 – 只是另一组模块。 查看所有那些(通常是令人讨厌的)I / O接口,它们使用有形基元来履行不同/独立的角色。 这自然(并且几乎神奇地)降低了所有组件(例如类)的个体复杂性。

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

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

反击:人们总能提出表现论证来反对这种思路。 根据我的经验,我发现JIT将优化和内联性能方面的任何内容(特别是对于平等比较等微不足道的事情),当它不存在时,几乎总是以一种理智的方式抽象性能关键代码。 如果调度是一个问题,请不要犹豫密封您的类型。

在测量并找到正确设计中的瓶颈之后 ,无法通过替代但同样理智的设计来解决,那么需要诚实地说明它们遇到语言限制这一事实,在这种情况下,应该考虑提取该代码并用更合适的语言编写或者使用现有语言进行黑客攻击,但要对该代码进行评论。

总而言之,我会提到我实际上完全按照你过去的建议行事,我只能感激所讨论的代码从未投入生产。 做一次并且它似乎没有害处,但是在整个地方都做到这一点并且不安的感觉安顿下来并且只会产生挫败感,直到你终于意识到为什么它是错的(根据上面的考虑)。 如果你很好奇,我为表示几乎总是包裹单个基元的实体键的类型做了这个。 再一次,它似乎是一个明智的想法,它可能看起来像你,但我坚信它现在不是现在,即使我以前不会听到它。 也许这是你需要在它最终沉入之前燃烧自己的东西之一。