Tengo un método genérico definido así:
public void MyMethod<T>(T myArgument)
Lo primero que quiero hacer es verificar si el valor de myArgument es el valor predeterminado para ese tipo, algo como esto:
if (myArgument == default(T))
Pero esto no se compila porque no he garantizado que T implemente el operador ==. Entonces cambié el código a esto:
if (myArgument.Equals(default(T)))
Ahora esto se compila, pero fallará si myArgument es nulo, lo cual es parte de lo que estoy probando. Puedo agregar un cheque nulo explícito como este:
if (myArgument == null || myArgument.Equals(default(T)))
Ahora esto me parece redundante. ReSharper incluso sugiere que cambie la parte nula myArgument == en myArgument == default (T), que es donde comencé. ¿Hay una mejor manera de resolver este problema?
Tengo que apoyar ambos tipos de referencias y tipos de valor.
if (myArgument?.Equals( default(T) ) != null )
.true
en cualquier caso porqueEquals
siempre se llamará para los tipos de valor yamyArgument
que no puede sernull
en este caso y el resultado deEquals
(un valor booleano) nunca lo seránull
.Respuestas:
Para evitar el boxeo, la mejor manera de comparar genéricos para la igualdad es con
EqualityComparer<T>.Default
. Esto respetaIEquatable<T>
(sin boxeo)object.Equals
y maneja todos losNullable<T>
matices "elevados". Por lo tanto:Esto coincidirá con:
Nullable<T>
fuente
Person
,p1.Equals(p2)
dependería de si se implementaIEquatable<Person>
en la API pública o mediante una implementación explícita, es decir, si el compilador puede ver unEquals(Person other)
método público . Sin embargo; en genéricos , se usa la misma IL para todosT
; UnaT1
implementación queIEquatable<T1>
debe implementarse debe tratarse de manera idéntica a unaT2
que no lo hace, por lo que no, no detectará unEquals(T1 other)
método, incluso si existe en tiempo de ejecución. En ambos casos, también haynull
que pensar (cualquier objeto). Entonces, con los genéricos, usaría el código que publiqué.Qué tal esto:
El uso del
static object.Equals()
método evita la necesidad de que usted mismo haga lanull
verificación. La calificación explícita de la llamadaobject.
probablemente no sea necesaria según su contexto, pero normalmente prefijo lasstatic
llamadas con el nombre del tipo solo para hacer que el código sea más soluble.fuente
Pude localizar un artículo de Microsoft Connect que analiza este problema con cierto detalle:
Esto es lo que puedes hacer ...
He validado que ambos métodos funcionan para una comparación genérica de tipos de referencia y valor:
o
Para hacer comparaciones con el operador "==", deberá utilizar uno de estos métodos:
Si todos los casos de T derivan de una clase base conocida, puede informar al compilador utilizando restricciones de tipo genérico.
El compilador luego reconoce cómo realizar operaciones
MyBase
y no arrojará el "Operador '==" no se puede aplicar a los operandos de tipo' T 'y' T '' error que está viendo ahora.Otra opción sería restringir T a cualquier tipo que implemente
IComparable
.Y luego use el
CompareTo
método definido por la interfaz IComparable .fuente
Prueba esto:
eso debería compilar y hacer lo que quieras.
fuente
Equals
método deIEqualityComparer
toma dos argumentos, los dos objetos para comparar, así que no, no es redundante.(Editado)
Marc Gravell tiene la mejor respuesta, pero quería publicar un fragmento de código simple que trabajé para demostrarlo. Simplemente ejecute esto en una aplicación de consola C # simple:
Una cosa más: ¿alguien con VS2008 puede probar esto como un método de extensión? Estoy atrapado con 2005 aquí y tengo curiosidad por ver si eso estaría permitido.
Editar: Aquí se explica cómo hacer que funcione como un método de extensión:
fuente
Para manejar todos los tipos de T, incluso cuando T es un tipo primitivo, deberá compilar en ambos métodos de comparación:
fuente
Va a haber un problema aquí.
Si va a permitir que esto funcione para cualquier tipo, el valor predeterminado (T) siempre será nulo para los tipos de referencia y 0 (o estructura llena de 0) para los tipos de valor.
Sin embargo, este probablemente no sea el comportamiento que buscas. Si desea que esto funcione de manera genérica, probablemente necesite usar la reflexión para verificar el tipo de T y manejar tipos de valores diferentes a los tipos de referencia.
Alternativamente, podría poner una restricción de interfaz en esto, y la interfaz podría proporcionar una forma de verificar el valor predeterminado de la clase / estructura.
fuente
Creo que probablemente necesites dividir esta lógica en dos partes y verificar primero si es nulo.
En el método IsNull, confiamos en el hecho de que los objetos ValueType no pueden ser nulos por definición, por lo que si el valor es una clase que se deriva de ValueType, ya sabemos que no es nulo. Por otro lado, si no es un tipo de valor, entonces podemos comparar el valor emitido con un objeto contra nulo. Podríamos evitar la comprobación de ValueType yendo directamente a un objeto de conversión, pero eso significaría que un tipo de valor quedaría encuadrado, lo que probablemente es algo que queremos evitar, ya que implica que se crea un nuevo objeto en el montón.
En el método IsNullOrEmpty, estamos buscando el caso especial de una cadena. Para todos los demás tipos, estamos comparando el valor (que ya sabemos que no es nulo) con su valor predeterminado, que para todos los tipos de referencia es nulo y para los tipos de valor suele ser alguna forma de cero (si son integrales).
Con estos métodos, el siguiente código se comporta como es de esperar:
fuente
Método de extensión basado en la respuesta aceptada.
Uso:
Alterne con nulo para simplificar:
Uso:
fuente
Yo suelo:
fuente
No sé si esto funciona con sus requisitos o no, pero podría restringir a T para que sea un Tipo que implemente una interfaz como IComparable y luego usar el método ComparesTo () desde esa interfaz (que IIRC admite / maneja nulos) como este :
Probablemente hay otras interfaces que podría usar también IEquitable, etc.
fuente
@ilitirit:
El operador '==' no se puede aplicar a operandos de tipo 'T' y 'T'
No puedo pensar en una manera de hacer esto sin la prueba nula explícita seguida de invocar el método u objeto Equals. Igual como se sugirió anteriormente.
Puede idear una solución usando System.Comparison pero realmente eso terminará con más líneas de código y aumentará la complejidad sustancialmente.
fuente
Creo que estabas cerca.
Ahora esto se compila, pero fallará si
myArgument
es nulo, lo cual es parte de lo que estoy probando. Puedo agregar un cheque nulo explícito como este:Solo necesita revertir el objeto en el que se llama a los iguales para un enfoque elegante de seguridad nula.
fuente