Mientras aprendía C #, descubrí que C # admite la sobrecarga del operador. Tengo un problema con un buen ejemplo que:
- Tiene sentido (por ejemplo, agregar una clase llamada oveja y vaca)
- No es un ejemplo de concatenación de dos cadenas.
Los ejemplos de Base Class Library son bienvenidos.
==
para multiplicar, ¡tiene sentido para mí, pero puede que no tenga sentido para otros! ¿Es esta pregunta sobre la legitimidad de qué lenguajes de programación de instalaciones o estamos hablando de 'mejores prácticas de codificación'?Respuestas:
Los ejemplos obvios de sobrecarga apropiada del operador son las clases que se comportan de la misma manera que operan los números. Por lo tanto, las clases BigInt (como sugiere Jalayn ), los números complejos o las clases matriciales (como sugiere Superbest ) tienen las mismas operaciones que los números ordinarios, por lo que se asignan muy bien a los operadores matemáticos, mientras que las operaciones de tiempo (como lo sugiere svick ) se asignan muy bien a un subconjunto de esas operaciones.
Un poco más abstracto, los operadores podrían usarse al realizar operaciones de conjunto , por lo que
operator+
podría ser una unión ,operator-
podría ser un complemento, etc. Sin embargo, esto comienza a estirar el paradigma, especialmente si usa el operador de suma o multiplicación para una operación que no es t conmutativo , como es de esperar que sean.C # en sí tiene un excelente ejemplo de sobrecarga de operadores no numéricos . Utiliza
+=
y-=
para sumar y restar delegados , es decir, registrarlos y anular su registro. Esto funciona bien porque los operadores+=
y-=
funcionan como cabría esperar, y esto da como resultado un código mucho más conciso.Para el purista, uno de los problemas con el
+
operador de cadena es que no es conmutativo."a"+"b"
No es lo mismo que"b"+"a"
. Entendemos esta excepción para las cadenas porque es muy común, pero ¿cómo podemos saber si el usooperator+
en otros tipos será conmutativo o no? La mayoría de las personas supondrá que lo es, a menos que el objeto tenga forma de cadena , pero nunca se sabe realmente qué asumirá la gente.Al igual que con las cadenas, las debilidades de las matrices también son bastante conocidas. Es obvio que
Matrix operator* (double, Matrix)
es una multiplicación escalar, mientrasMatrix operator* (Matrix, Matrix)
que sería una multiplicación matricial (es decir, una matriz de multiplicaciones de productos de puntos), por ejemplo.Del mismo modo, el uso de operadores con delegados está tan alejado de las matemáticas que es poco probable que cometa esos errores.
Por cierto, en la conferencia ACCU 2011 , Roger Orr y Steve Love presentaron una sesión sobre Algunos objetos son más iguales que otros: una mirada a los muchos significados de igualdad, valor e identidad . Sus diapositivas se pueden descargar , al igual que el Apéndice de Richard Harris sobre la igualdad de coma flotante . Resumen: ¡ Ten mucho cuidado con
operator==
, aquí hay dragones!La sobrecarga del operador es una técnica semántica muy poderosa, pero es fácil de usar en exceso. Idealmente, solo debe usarlo en situaciones en las que está muy claro por contexto cuál es el efecto de un operador sobrecargado. En muchos sentidos
a.union(b)
es más claro quea+b
, ya*b
es mucho más oscuro quea.cartesianProduct(b)
, especialmente porque el resultado de un producto cartesiano seríaSetLike<Tuple<T,T>>
más bien un aSetLike<T>
.Los problemas reales con la sobrecarga del operador se producen cuando un programador supone que una clase se comportará de una manera, pero en realidad se comportará de otra. Este tipo de choque semántico es lo que sugiero que es importante tratar de evitar.
fuente
d1 + d2
para dos delegados del mismo tipo.Me sorprende que nadie haya mencionado uno de los casos más interesantes en BCL:
DateTime
yTimeSpan
. Usted puede:TimeSpan
s para obtener otroTimeSpan
TimeSpan
para obtener un negadoTimeSpan
DateTime
s para obtener unTimeSpan
TimeSpan
de unDateTime
para obtener otroDateTime
Otro conjunto de operadores que podría tener sentido en una gran cantidad de tipos son
<
,>
,<=
,>=
. En el BCL, por ejemplo, losVersion
implementa.fuente
El primer ejemplo que me viene a la mente es la implementación de BigInteger , que le permite trabajar con grandes enteros con signo. Consulte el enlace de MSDN para ver cuántos operadores se han sobrecargado (es decir, hay una lista grande y no verifiqué si todos los operadores se han sobrecargado, pero ciertamente parece que sí)
Además, como también hago Java y Java no permite la sobrecarga de operadores, es increíblemente más dulce escribir
Que, en Java:
fuente
Me alegro de haberlo visto porque he estado jugando con Irony y tiene un GRAN uso de la sobrecarga del operador. Aquí hay una muestra de lo que puede hacer.
Así que Irony es un ".NET Language Implementation Kit" y es un generador de analizador (que genera un analizador LALR). En lugar de tener que aprender una nueva sintaxis / lenguaje como generadores de analizadores sintácticos como yacc / lex, usted escribe la gramática en C # con la sobrecarga del operador. Aquí hay una gramática simple de BNF
Por lo tanto, es una pequeña gramática simple (disculpe si hay inconsistencias ya que solo estoy aprendiendo BNF y construyendo gramáticas). Ahora veamos el C #:
Como puede ver, con la sobrecarga del operador, escribir la gramática en C # es casi exactamente escribir la gramática en BNF. Para mí, eso no solo tiene sentido, sino que es un gran uso de la sobrecarga del operador.
fuente
El ejemplo clave es operator == / operator! =.
Si desea comparar fácilmente dos objetos con valores de datos en lugar de por referencia, querrá sobrecargar .Equals (y.GetHashCode!), Y puede querer hacer los operadores! = Y == también para mantener la coherencia.
Sin embargo, nunca he visto ninguna sobrecarga salvaje de otros operadores en C # (imagino que hay casos extremos en los que podría ser útil).
fuente
Este ejemplo de MSDN muestra cómo implementar números complejos y hacer que usen el operador normal +.
Otro ejemplo muestra cómo hacerlo para la suma de matrices, y también explica cómo no usarlo para agregar un automóvil a un garaje (lea el enlace).
fuente
El buen uso de la sobrecarga puede ser raro, pero sucede.
sobrecargar operator == y operator! = muestra dos escuelas de pensamiento: las que dicen que hacen las cosas más fáciles y las que no dicen que evitan comparar direcciones (es decir, estoy señalando exactamente el mismo lugar en la memoria, no solo una copia de la misma objeto).
Encuentro que las sobrecargas del operador de reparto son útiles en situaciones específicas. Por ejemplo, tuve que serializar / deserializar en XML un booleano representado como 0 o 1. El operador de conversión correcto (implícito o explícito, lo olvido) de booleano a int y viceversa hizo el truco.
fuente
object.ReferenceEquals()
.==
mediante la conversión:(object)foo == (object)bar
siempre compara referencias. Pero preferiríaReferenceEquals()
, como @ dan04 menciona porque es más claro lo que hace.No están en la categoría de cosas en las que la gente suele pensar cuando se trata de la sobrecarga del operador, pero creo que uno de los operadores más importantes para poder sobrecargar es el operador de conversión .
Los operadores de conversión son especialmente útiles para los tipos de valor que pueden "quitar el azúcar" a un tipo numérico, o pueden actuar como un tipo numérico en algunos contextos. Por ejemplo, puede definir un
Id
tipo especial que represente un determinado identificador, y puede proporcionar una conversión implícita aint
para que pueda pasar unId
método que tome unint
, pero una conversión explícita deint
aId
para que nadie pueda pasar unint
a método que toma unId
sin lanzarlo primero.Como ejemplo fuera de C #, el lenguaje Python incluye muchos comportamientos especiales que se implementan como operadores sobrecargables. Estos incluyen el
in
operador para la prueba de membresía, el()
operador para llamar a un objeto como si fuera una función y ellen
operador para determinar la longitud o el tamaño de un objeto.Y luego tiene idiomas como Haskell, Scala y muchos otros lenguajes funcionales, donde los nombres como
+
solo son funciones ordinarias, y no operadores en absoluto (y hay soporte de idiomas para usar funciones en posición infija).fuente
El Struct punto en el System.Drawing espacio de nombres utiliza la sobrecarga para comparar dos ubicaciones diferentes utilizando la sobrecarga de operadores.
Como puede ver, es mucho más fácil comparar las coordenadas X e Y de dos ubicaciones utilizando la sobrecarga.
fuente
Si está familiarizado con el vector matemático, puede ver un uso al sobrecargar el
+
operador. Puede agregar un vectora=[1,3]
conb=[2,-1]
y obtenerc=[3,2]
.Sobrecargar los iguales (==) también puede ser útil (aunque probablemente sea mejor implementar un
equals()
método). Para continuar con los ejemplos de vectores:fuente
Imagine una pieza de código para dibujar en un formulario
Otro ejemplo común es cuando se usa una estructura para contener información de posición en forma de un vector.
solo para ser usado más tarde como
fuente
operator+
debería no ser sobrecargado (se puede implementar un punto en términos de un vector, pero no debería ser capaz de añadir dos puntos)p1+((p2-p1)+(p3-p1)+(p4-p1))/4
, pero eso parece algo incómodo.