c# - example - ¿Por qué se pueden comparar TimeSpan y Guid Structs con null?
struct c# example (5)
Es el operador ==
.
La clase TimeSpan
tiene una sobrecarga del operador de igualdad:
public static bool operator ==(DateTime d1, DateTime d2)
{
return (t1._ticks == t2._ticks);
}
Esto en sí mismo no permite comparar con null
, pero ...
Con la llegada de tipos anulables, cada estructura se puede convertir implícitamente a su tipo anulable , de modo que cuando vea algo como
TimeSpan y = new TimeSpan();
if (y == null)
return;
No ves que esto está sucediendo.
TimeSpan y = new TimeSpan();
if ((Nullable<TimeSpan>)y == (Nullable<TimeSpan>)null)
return;
Null obtiene la conversión implícita (asignación implícita?), Pero no todos los objetos System.Object
:
TimeSpan y = new TimeSpan();
object o = null;
if (y == o) //compiler error
return;
Está bien, pero el operador de igualdad no acepta argumentos anulables, ¿verdad?
Bueno, msdn es de ayuda aquí, declarando:
Los tipos nulables también pueden utilizar los operadores unarios y binarios predefinidos y cualquier operador definido por el usuario que exista para tipos de valor. Estos operadores producen un valor nulo si [cualquiera de] los operandos son nulos; de lo contrario, el operador utiliza el valor contenido para calcular el resultado.
De este modo, obtiene de forma gratuita una implementación anulable para cada operador , con un comportamiento definido fijo. El "valor contenido" mencionado anteriormente es el valor real que devolvería el operador no anulable.
He notado que algunas estructuras .NET pueden compararse con nulas. Por ejemplo:
TimeSpan y = new TimeSpan();
if (y == null)
return;
compilará bien (lo mismo con la estructura Guid).
Ahora sé que los stucts son tipo de valor y que el código anterior no debe compilarse, a menos que haya una sobrecarga del operador == que toma un objeto. Pero, por lo que pude ver, no hay.
He mirado la clase con Reflector y también los documentos en MSDN.
Los dos implementan las siguientes interfaces:
IComparable, IComparable<T>, IEquatable<T>
pero, al intentar implementar las mismas Interfaces no pareció ayudar:
struct XX : IComparable, IComparable<XX>, IEquatable<XX> {
public int CompareTo(Object obj) {
return 0;
}
public int CompareTo (XX other){
return 0;
}
public bool Equals (XX other){
return false;
}
public override bool Equals(object value){
return false;
}
public static int Compare(XX t1, XX t2){
return 0;
}
}
Estoy usando: .NET 2.0 Visual Studio 2005.
¿Alguien tiene alguna idea de cuál es la razón de esto? Sólo estoy tratando de obtener una mejor comprensión. Esto no es un problema, ya que sé que no debería comparar las estructuras con nulas de todos modos.
Este caso está cubierto para los genéricos en la sección 7.9.6 de la especificación del lenguaje C #.
La construcción nula x == está permitida aunque T podría representar un tipo de valor, y el resultado simplemente se define como falso cuando T es un tipo de valor.
Revisé la especificación por un momento y no pude encontrar una regla más general. La answer de Jon indica que es un problema de promoción anulable.
Esta regla (o una variación similar) parece aplicarse aquí. Si observas detenidamente la salida reflejada, notarás que la comparación no está allí. El compilador de C # aparentemente está optimizando esta comparación y reemplazándola con falso.
Por ejemplo, si escribe lo siguiente
var x = new TimeSpan();
var y = x == null;
Console.WriteLine(x);
Luego descompilarlo verás lo siguiente.
var x = new TimeSpan();
var y = false;
Console.WriteLine(x);
Este problema se introdujo efectivamente cuando se incluyeron tipos anulables. ¿Hay una conversión implícita de TimeSpan
a TimeSpan?
, y hay una comparación entre TimeSpan?
y el valor nulo de ese tipo.
El compilador emite una advertencia para algunos tipos que aclara lo que está tratando de hacer:
int x = 10;
if (x == null)
{
Console.WriteLine();
}
Da esta advertencia:
Test.cs(9,13): warning CS0472: The result of the expression is always ''false''
since a value of type ''int'' is never equal to ''null'' of type ''int?''
Creo que Marc Gravell y yo resolvimos las circunstancias en las que se da la advertencia una vez ... es una pena que no sea coherente.
Ver también: La versión C # 3 (.NET 3.5) de csc no puede reportar CS0162 para código irrenable (struct / null)
Comenzando con el compilador C # 3, eso significa que a veces ni siquiera te advierte acerca de esto ;-p
Debido a que Guid
/ TimeSpan
etc. proporciona ==
, caen en esta trampa donde no te advierte.
Lo encontré :)
Lo siguiente da una advertencia:
int i = 0;
if (i == null)
// ^^ Warning: The result of the expression is always ''false'' since a value of
// type ''int'' is never equal to ''null'' of type ''int?''
¿El compilador simplemente no emite la advertencia correcta de que el null
que escribió se convirtió al tipo TimeSpan?
para la comparacion
Edición: la sección relacionada en la especificación es §13.7.1 que indica que el null
se puede convertir implícitamente a cualquier tipo que pueda contener null
, y (la muy difícil de leer) la sección §13.7.2 que indica que un tipo de valor T
se puede convertir implícitamente a T?
.
Lo que originalmente escribí:
Lo que sea que esté sucediendo es algo en la especificación de C # porque al igual que JaredPar dice que se compila simplemente como false
.
Tenga en cuenta que esto no se compila:
TimeSpan ts = new TimeSpan();
object o = null;
if (ts == o) // error, Operator ''=='' cannot be applied to operands of type ''System.TimeSpan'' and ''object''
...