De acuerdo con la documentación del ==
operador en MSDN ,
Para los tipos de valores predefinidos, el operador de igualdad (==) devuelve verdadero si los valores de sus operandos son iguales, de lo contrario, falso. Para los tipos de referencia que no sean cadenas, == devuelve verdadero si sus dos operandos se refieren al mismo objeto. Para el tipo de cadena, == compara los valores de las cadenas. Los tipos de valores definidos por el usuario pueden sobrecargar el operador == (ver operador). También pueden los tipos de referencia definidos por el usuario, aunque por defecto == se comporta como se describe anteriormente para los tipos de referencia predefinidos y definidos por el usuario.
Entonces, ¿por qué este fragmento de código no se compila?
bool Compare<T>(T x, T y) { return x == y; }
Me sale el error Operador '==' no se puede aplicar a operandos de tipo 'T' y 'T' . Me pregunto por qué, ya que entiendo que el ==
operador está predefinido para todos los tipos.
Editar: Gracias a todos. Al principio no me di cuenta de que la declaración se refería solo a los tipos de referencia. También pensé que se proporciona una comparación bit a bit para todos los tipos de valores, lo que ahora sé que no es correcto.
Pero, en caso de que esté usando un tipo de referencia, ¿usaría el ==
operador la comparación de referencia predefinida, o usaría la versión sobrecargada del operador si un tipo definiera uno?
Edición 2: a través de prueba y error, aprendimos que el ==
operador usará la comparación de referencia predefinida cuando use un tipo genérico sin restricciones. En realidad, el compilador utilizará el mejor método que pueda encontrar para el argumento de tipo restringido, pero no buscará más. Por ejemplo, el siguiente código siempre se imprimirá true
, incluso cuando Test.test<B>(new B(), new B())
se llama:
class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }
fuente
==
no está permitido entre dos operandos del mismo tipo. Esto es cierto para losstruct
tipos (excepto los tipos "predefinidos") que no sobrecargan eloperator ==
. Como ejemplo simple, intente esto:var map = typeof(string).GetInterfaceMap(typeof(ICloneable)); Console.WriteLine(map == map); /* compile-time error */
var kvp1 = new KeyValuePair<int, int>(); var kvp2 = kvp1;
, entonces no puede verificarkvp1 == kvp2
porqueKeyValuePair<,>
es una estructura, no es un tipo predefinido de C # y no sobrecarga eloperator ==
. Sin embargo, se proporciona un ejemplovar li = new List<int>(); var e1 = li.GetEnumerator(); var e2 = e1;
con el que no puede hacerloe1 == e2
(aquí tenemos la estructura anidadaList<>.Enumerator
(llamada"List`1+Enumerator[T]"
por el tiempo de ejecución) que no se sobrecarga==
).bool
devoid
...Respuestas:
"... por defecto == se comporta como se describe anteriormente para los tipos de referencia predefinidos y definidos por el usuario".
El tipo T no es necesariamente un tipo de referencia, por lo que el compilador no puede hacer esa suposición.
Sin embargo, esto se compilará porque es más explícito:
Siga a la pregunta adicional: "Pero, en caso de que esté usando un tipo de referencia, ¿el operador == usaría la comparación de referencia predefinida, o usaría la versión sobrecargada del operador si un tipo definiera uno?"
Pensé que == en los genéricos usaría la versión sobrecargada, pero la siguiente prueba demuestra lo contrario. Interesante ... ¡Me encantaría saber por qué! Si alguien sabe por favor comparte.
Salida
En línea: sobrecargado == llamado
Genérico:
Pulse cualquier tecla para continuar . . .
Seguimiento 2
Quiero señalar que cambiar mi método de comparación a
hace que se llame al operador == sobrecargado. Supongo que sin especificar el tipo (como un lugar ), el compilador no puede inferir que debería usar el operador sobrecargado ... aunque creo que tendría suficiente información para tomar esa decisión, incluso sin especificar el tipo.
fuente
Equals
método anulado (no en el==
operador).==
entre tipos genéricosT
yT
, se encuentra la mejor sobrecarga, dadas las restricciones que conllevaT
(hay una regla especial que nunca incluirá un tipo de valor para esto (lo que daría un resultado sin sentido), por lo tanto, debe haber alguna restricción que garantiza que es un tipo de referencia). En su Seguimiento 2 , si ingresa conDerivedTest
objetos y seDerivedTest
deriva deTest
una nueva sobrecarga==
, pero presenta una nueva sobrecarga , volverá a tener el "problema". La sobrecarga que se llama se "quema" en la IL en tiempo de compilación.Como han dicho otros, solo funcionará cuando T esté limitado a ser un tipo de referencia. Sin restricciones, puede comparar con nulo, pero solo nulo, y esa comparación siempre será falsa para los tipos de valores no anulables.
En lugar de llamar a Equals, es mejor usar un
IComparer<T>
, y si no tiene más información,EqualityComparer<T>.Default
es una buena opción:Aparte de cualquier otra cosa, esto evita el boxeo / lanzamiento.
fuente
En general,
EqualityComparer<T>.Default.Equals
debe hacer el trabajo con cualquier cosa que implementeIEquatable<T>
, o que tenga unaEquals
implementación sensata .Si, sin embargo,
==
yEquals
se implementan de forma diferente por alguna razón, entonces mi trabajo sobre operadores genéricos debe ser útil; admite las versiones de operador de (entre otros):fuente
Tantas respuestas, y ninguna explica el ¿POR QUÉ? (que Giovanni preguntó explícitamente) ...
Los genéricos .NET no actúan como plantillas de C ++. En las plantillas de C ++, la resolución de sobrecarga ocurre después de que se conocen los parámetros reales de la plantilla.
En los genéricos .NET (incluido C #), la resolución de sobrecarga ocurre sin conocer los parámetros genéricos reales. La única información que el compilador puede usar para elegir la función a llamar proviene de restricciones de tipo en los parámetros genéricos.
fuente
==
funciona para todos los tipos, ya sean tipos de referencia o tipos de valores. Esa debería ser la pregunta a la que no creo que hayas respondido.==
no funciona para todos los tipos de valor. Más importante aún, no tiene el mismo significado para todos los tipos, por lo que el compilador no sabe qué hacer con él.==
. Se puede incluir también esa parte de su respuesta ya que supongo que ese es el punto principal aquíLa compilación no puede saber que T no podría ser una estructura (tipo de valor). Entonces, debes decir que solo puede ser del tipo de referencia, creo:
Esto se debe a que si T podría ser un tipo de valor, podría haber casos en los
x == y
que estaría mal formado, en los casos en que un tipo no tiene un operador == definido. Lo mismo sucederá para esto, que es más obvio:Eso también falla, porque podrías pasar un tipo T que no tendría una función foo. C # lo obliga a asegurarse de que todos los tipos posibles siempre tengan una función foo. Eso se hace con la cláusula where.
fuente
Parece que sin la restricción de clase:
Uno debe darse cuenta de que mientras está
class
restringidoEquals
en el==
operador heredaObject.Equals
, mientras que el de una estructura anulaValueType.Equals
.Tenga en cuenta que:
también da el mismo error de compilación.
Hasta ahora no entiendo por qué el compilador rechaza tener una comparación de operador de igualdad de tipo de valor. Sin embargo, sí sé que esto funciona:
fuente
Object.Equals
sino que prueba la igualdad de referencia. Por ejemplo,Compare("0", 0.ToString())
devolvería falso, ya que los argumentos serían referencias a cadenas distintas, las cuales tienen un cero como único carácter.NullReferenceException
podría suceder.Bueno, en mi caso, quería probar unitariamente el operador de igualdad. Necesitaba llamar al código bajo los operadores de igualdad sin establecer explícitamente el tipo genérico. Los consejos para
EqualityComparer
no fueron útiles como métodoEqualityComparer
llamadoEquals
, pero no el operador de igualdad.Así es como he conseguido que esto funcione con tipos genéricos mediante la construcción de un
LINQ
. Llama al código correcto para==
y!=
operadores:fuente
Hay una entrada de MSDN Connect para esto aquí
La respuesta de Alex Turner comienza con:
fuente
Si desea asegurarse de que se llame a los operadores de su tipo personalizado, puede hacerlo mediante reflexión. Simplemente obtenga el tipo usando su parámetro genérico y recupere el MethodInfo para el operador deseado (por ejemplo, op_Equality, op_Inequality, op_LessThan ...).
Luego ejecute el operador utilizando el método de invocación MethodInfo y pase los objetos como parámetros.
Esto invocará a su operador sobrecargado y no al definido por las restricciones aplicadas en el parámetro genérico. Puede que no sea práctico, pero podría ser útil para las pruebas unitarias de sus operadores cuando se utiliza una clase base genérica que contiene un par de pruebas.
fuente
Escribí la siguiente función mirando el último msdn. Puede comparar fácilmente dos objetos
x
yy
:fuente
return ((IComparable)(x)).CompareTo(y) <= 0;
Lo anterior funcionará porque == se atiende en caso de tipos de referencia definidos por el usuario.
En el caso de los tipos de valor, == se puede anular. En cuyo caso, "! =" También debe definirse.
Creo que esa podría ser la razón, no permite la comparación genérica con "==".
fuente
==
token se usa para dos operadores diferentes. Si para los tipos de operandos dados existe una sobrecarga compatible del operador de igualdad, se utilizará esa sobrecarga. De lo contrario, si ambos operandos son tipos de referencia compatibles entre sí, se utilizará una comparación de referencia. Tenga en cuenta que en elCompare
método anterior, el compilador no puede decir que se aplica el primer significado, pero puede decir que se aplica el segundo significado, por lo que el==
token usará el último incluso siT
sobrecarga el operador de verificación de igualdad (por ejemplo, si es de tipoString
) .los
.Equals()
funciona para mí mientras queTKey
es un tipo genérico.fuente
x.Id.Equals
noid.Equals
. Presumiblemente, el compilador sabe algo sobre el tipo dex
.