Sobrecarga del operador y diferentes tipos.

Tengo un puntaje de clase que se usará mucho en comparaciones con enteros. Estaba planeando sobrecargar el operador == para habilitar estas comparaciones según el código a continuación?

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

¿Es este un uso sensato de la sobrecarga del operador?

¿Debo proporcionar sobrecargas para los lados izquierdo y derecho de los operadores para permitir que el uso sea simétrico?

Podría seguir adelante y definir una conversión implícita de int a Score , de modo que cuando se trata de igualdad, solo necesite tratar con un solo tipo.

 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); 

Y mientras está definiendo su propio operador == , recuerde seguir adelante y definir != , Y anular Equals y GetHashCode .

Generalmente cuando veo un operador sobrecargado, compara esa clase con sí misma. Entonces, si tiene múltiples variables de instancia, debería averiguar cómo comparar las dos para determinar si algo era igual, mayor, menor, etc.

No veo por qué no simplemente haces:

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

Creo que esta es una situación extraña para usar una sobrecarga de operador, pero es su llamada.

Sin embargo, mi punto principal es que si sobrecargas == también se te pedirá que sobrecargues! =

Si luego sobrecarga! =, La parte donde compara x para verificar que no sea nula usando x != null hará que el operador == llame al operador! =. Esto no es un problema en sí mismo, siempre y cuando no utilice una comparación ==, ya que tendrá un conjunto recursivo de llamadas, lo que llevará a un desbordamiento de stack.

Sin embargo, debido a que mucha gente al sobrecargar = implementarlo como ‘no ==’, en su caso esto causará un desbordamiento de stack.

Solución: particularmente en la sobrecarga == Object.ReferenceEquals(x, null); = Y Equals (), su mejor uso es usar Object.ReferenceEquals(x, null); Cuando se compara a nulo.

“¿Es este un uso sensato de la sobrecarga del operador?”

Si hace que su código sea más fácil de entender y no cause problemas, yo diría que sí, ¡absolutamente! También puede haber otras formas, pero si esto funciona para usted, no veo un problema.

“¿Debo proporcionar sobrecargas para los lados izquierdo y derecho de los operadores para permitir que el uso sea simétrico?”

Yo diría que, a menos que tenga una razón específica para hacerlo, en otras palabras, si los está usando o los necesita, entonces no los va a necesitar. (YAGNI) La única excepción real que veo es si estás escribiendo un marco en el que tienes una buena idea de que alguien más lo necesitará.

tl; dr En el gran esquema de las cosas; No, no es una idea sensata.

  1. El hecho de que uno sienta la necesidad de preguntar o buscar esta pregunta ya es revelador.
  2. El hecho de que a pocas personas parece importarle indica que podría haber una manera de simplemente evitar la situación en la que necesita hacer la pregunta para comenzar.
  3. Estos dos últimos puntos deberían al menos obligarte a reconsiderar algunas cosas, por más que estén relacionadas.

Considere asignar tipos a todas las entradas a su componente de software tan pronto como sea posible y fuera de la lógica de su dominio. De esta manera, lo que comúnmente llamamos “modelo de dominio” nunca tiene que preocuparse por esto.

En su caso, eso significa envolver todas las puntuaciones en los objetos Score . Porque eso es lo que son. La necesidad de hacer excepciones siempre es al menos dudosa y califica como un olor de código.

El siguiente paso es ver su “modelo de dominio” como nada especial, simplemente otro conjunto de módulos. Vea todas esas interfaces de E / S (que a menudo son desagradables) que utilizan primitivas tangibles para cumplir una función distinta / separada. Esto naturalmente (y casi mágicamente) reducirá la complejidad individual de todos sus componentes (por ejemplo, clases).

Un efecto secundario de esto es que puede descubrir que esta misma pregunta no tiene sentido. “¿Por qué necesitaría comparar un puntaje con un entero primitivo si ni siquiera estoy seguro de que ese entero sea un puntaje? Y si estoy seguro, ¿por qué no lo dije antes (p. Ej., Encajándolo)? ”

Yendo aún más lejos, podría darse cuenta de que muchos de sus algoritmos ni siquiera necesitan saber cómo se mantiene una puntuación: es probable que la mayoría de ellos solo necesiten poder compararlos para ver si son iguales o para ver cuál es mayor. No estoy diciendo que la flexibilidad de modificar la forma en que se mantiene la puntuación será útil en el futuro, pero como regla general, si no debería importar , no es necesario vincular el código a una implementación solo por ser lindo. Quizás sorprendentemente, eso también se aplica a primitivas como los enteros, e incluso al concepto mismo de un número (que no es necesario que cuide en todos los casos en que todo lo que necesita es comparar dos puntajes). Los principios de SOLID proporcionan una buena guía hasta que pueda desarrollar intuiciones por sí mismo.

Contador: siempre se puede presentar el argumento de rendimiento para argumentar en contra de esta línea de pensamiento. En mi experiencia, encontré que el JIT optimizará y en línea todo lo que se interponga en el rendimiento (especialmente con cosas triviales como las comparaciones de igualdad) y cuando no es así, casi siempre hay una forma de abstraer el código crítico de rendimiento de una manera sensata. No dude en sellar sus tipos si el envío es un problema.

Después de medir y encontrar un cuello de botella en un diseño correcto que no puede ser resuelto por un diseño alternativo pero igualmente sensato, entonces uno tiene que ser sincero sobre el hecho de que tienen un límite de idioma , en cuyo caso uno debería considerar extraer ese código. y escríbalo en un lenguaje más apropiado o piratee el lenguaje existente, pero comente el código.

Para concluir, mencionaré que en realidad hice exactamente lo que sugirió en el pasado, y solo puedo estar agradecido de que el código en cuestión nunca entró en producción. Hazlo una vez y no parece perjudicial, pero hazlo por todos lados y esa sensación incómoda se instala y solo genera frustración hasta que finalmente te das cuenta de por qué está mal (según las consideraciones anteriores). Si tienes curiosidad, hice esto para tipos que representaban claves de entidad que casi siempre envolvían una primitiva única. Una vez más, me pareció una idea sensata y aún podría parecerlo a usted, pero creo firmemente que no lo es ahora, aunque no lo haya escuchado antes. Tal vez es una de esas cosas con las que necesitas quemarte antes de que finalmente se hunda.