¿No se puede aplicar el operador == a tipos genéricos en C#?
Según la documentación del ==
operador en MSDN ,
Para tipos de valores predefinidos, el operador de igualdad (==) devuelve verdadero si los valores de sus operandos son iguales, falso en caso contrario. Para tipos de referencia distintos de cadena, == 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 hacerlo los tipos de referencia definidos por el usuario, aunque de forma predeterminada == se comporta como se describe anteriormente tanto para los tipos de referencia predefinidos como para los definidos por el usuario.
Entonces, ¿por qué no se puede compilar este fragmento de código?
bool Compare<T>(T x, T y) { return x == y; }
Recibo el error El operador '==' no se puede aplicar a operandos de tipo 'T' y 'T' . Me pregunto por qué, según tengo entendido, 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 únicamente a tipos de referencia. También pensé que se proporciona una comparación bit a bit para todos los tipos de valores, lo cual ahora sé que no es correcto.
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 una?
Edición 2: mediante prueba y error, aprendimos que el ==
operador utilizará la comparación de referencia predefinida cuando utilice 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 imprimirá true
, incluso cuando Test.test<B>(new B(), new B())
se llame:
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); } }
Como han dicho otros, solo funcionará cuando T esté obligado a ser un tipo de referencia. Sin ninguna restricción, puede comparar con nulo, pero solo con nulo, y esa comparación siempre será falsa para los tipos de valores que no aceptan nulos.
En lugar de llamar a Equals, es mejor usar un IComparer<T>
- y si no tienes más información, EqualityComparer<T>.Default
es una buena opción:
public bool Compare<T>(T x, T y)
{
return EqualityComparer<T>.Default.Equals(x, y);
}
Aparte de cualquier otra cosa, esto evita el boxeo/casting.
"... 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:
bool Compare<T>(T x, T y) where T : class
{
return x == y;
}
Continúe con 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 una?"
Habría pensado que == en Generics usaría la versión sobrecargada, pero la siguiente prueba demuestra lo contrario. Interesante... ¡Me encantaría saber por qué! Si alguien lo sabe por favor comparta.
namespace TestProject
{
class Program
{
static void Main(string[] args)
{
Test a = new Test();
Test b = new Test();
Console.WriteLine("Inline:");
bool x = a == b;
Console.WriteLine("Generic:");
Compare<Test>(a, b);
}
static bool Compare<T>(T x, T y) where T : class
{
return x == y;
}
}
class Test
{
public static bool operator ==(Test a, Test b)
{
Console.WriteLine("Overloaded == called");
return a.Equals(b);
}
public static bool operator !=(Test a, Test b)
{
Console.WriteLine("Overloaded != called");
return a.Equals(b);
}
}
}
Producción
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
static bool Compare<T>(T x, T y) where T : Test
{
return x == y;
}
hace que se llame al operador == sobrecargado. Supongo que sin especificar el tipo (como dónde ), el compilador no puede inferir que debe usar el operador sobrecargado... aunque creo que tendría suficiente información para tomar esa decisión incluso sin especificar el tipo.
En general, EqualityComparer<T>.Default.Equals
debería funcionar con cualquier cosa que implemente IEquatable<T>
o que tenga una Equals
implementación sensata.
Sin embargo, si ==
y Equals
se implementan de manera diferente por alguna razón, entonces mi trabajo sobre operadores genéricos debería ser útil; admite las versiones de operador de (entre otras):
- Igual (valor T1, valor T2)
- No igual (valor T1, valor T2)
- Mayor que (valor T1, valor T2)
- Menos que(valor T1, valor T2)
- Mayor que o igual (valor T1, valor T2)
- MenosThanOrEqual(valor T1, valor T2)