valor - validar fecha null en c#
C#está bien al comparar tipos de valores con nulos (9)
Me encontré con esto hoy y no tengo idea de por qué el compilador de C # no está arrojando un error.
Int32 x = 1;
if (x == null)
{
Console.WriteLine("What the?");
}
Estoy confundido sobre cómo x alguna vez podría ser nulo. Especialmente porque esta asignación definitivamente arroja un error de compilación:
Int32 x = null;
¿Es posible que x se vuelva nulo? ¿Microsoft simplemente decidió no colocar esta verificación en el compilador o se perdió por completo?
Actualización: después de jugar con el código para escribir este artículo, de repente el compilador apareció con una advertencia de que la expresión nunca sería cierta. Ahora estoy realmente perdido. Puse el objeto en una clase y ahora la advertencia se ha ido pero se fue con la pregunta, ¿puede un tipo de valor ser nulo?
public class Test
{
public DateTime ADate = DateTime.Now;
public Test ()
{
Test test = new Test();
if (test.ADate == null)
{
Console.WriteLine("What the?");
}
}
}
Creo que la mejor respuesta sobre por qué el compilador acepta esto es para las clases genéricas. Considere la siguiente clase ...
public class NullTester<T>
{
public bool IsNull(T value)
{
return (value == null);
}
}
Si el compilador no aceptara comparaciones con null
para los tipos de valor, esencialmente rompería esta clase, con una restricción implícita asociada a su parámetro de tipo (es decir, solo funcionaría con tipos no basados en el valor).
El hecho de que una comparación nunca puede ser cierta no significa que sea ilegal. Sin embargo, no, un tipo de valor puede ser null
alguna vez.
Esto es legal porque la resolución de sobrecarga del operador tiene un mejor operador único para elegir. Hay un operador == que toma dos entradas anulables. El int local es convertible a un int nullable. El literal nulo es convertible a un int nullable. Por lo tanto, este es un uso legal del operador ==, y siempre dará como resultado falso.
Del mismo modo, también te permitimos decir "if (x == 12.6)", que también siempre será falso. El int local es convertible a doble, el literal es convertible a doble, y obviamente nunca serán iguales.
No es un error, ya que hay una conversión ( int?
); genera una advertencia en el ejemplo dado:
El resultado de la expresión es siempre ''falso'' ya que un valor de tipo ''int'' nunca es igual a ''nulo'' de tipo ''int'' ''
Si marca el IL, verá que elimina completamente la rama inalcanzable, no existe en una compilación de lanzamiento.
Sin embargo, tenga en cuenta que no genera esta advertencia para estructuras personalizadas con operadores de igualdad. Solía en 2.0, pero no en el compilador 3.0. El código aún se elimina (por lo que sabe que el código no está disponible), pero no se genera ninguna advertencia:
using System;
struct MyValue
{
private readonly int value;
public MyValue(int value) { this.value = value; }
public static bool operator ==(MyValue x, MyValue y) {
return x.value == y.value;
}
public static bool operator !=(MyValue x, MyValue y) {
return x.value != y.value;
}
}
class Program
{
static void Main()
{
int i = 1;
MyValue v = new MyValue(1);
if (i == null) { Console.WriteLine("a"); } // warning
if (v == null) { Console.WriteLine("a"); } // no warning
}
}
Con el IL (para Main
), tenga en cuenta que se ha eliminado todo, excepto MyValue(1)
(que podría tener efectos secundarios):
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 i,
[1] valuetype MyValue v)
L_0000: ldc.i4.1
L_0001: stloc.0
L_0002: ldloca.s v
L_0004: ldc.i4.1
L_0005: call instance void MyValue::.ctor(int32)
L_000a: ret
}
esto es básicamente:
private static void Main()
{
MyValue v = new MyValue(1);
}
No, Int32 x
nunca se convertirá en null
.
Si está comparando un int con un nulo, entonces el operador de comparación que toma dos int es aplicable.
"¿Por qué una comparación de un tipo de valor con nulo es una advertencia?" el artículo te ayudará.
Sospecho que el compilador optimiza la prueba en particular cuando genera el IL ya que la prueba nunca será falsa.
Nota al margen: ¿Es posible tener un Int32 nulo que use Int32? x en su lugar.
Supongo que esto se debe a que "==" es un azúcar de sintaxis que en realidad representa una llamada al método System.Object.Equals
que acepta el parámetro System.Object
. La especificación NULL by ECMA es un tipo especial que, por supuesto, deriva de System.Object
.
Es por eso que solo hay una advertencia.
Un tipo de valor no puede ser null
, aunque podría ser igual a null
(considere Nullable<>
). En su caso, la variable int
y null
se Nullable<Int32>
implícitamente en Nullable<Int32>
y se comparan.
[EDITADO: hizo advertencias sobre errores e hizo explícitos a los operadores sobre anulables en lugar del hack de cadenas.]
Según la sugerencia inteligente de @ supercat en un comentario anterior, las siguientes sobrecargas del operador le permiten generar un error sobre las comparaciones de su tipo de valor personalizado a nulo.
Mediante la implementación de operadores que se comparan con las versiones anulables de su tipo, el uso de null en una comparación coincide con la versión anulable del operador, que le permite generar el error a través del atributo Obsoleto.
Hasta que Microsoft nos devuelva nuestra advertencia del compilador, voy con esta solución, ¡gracias @supercat!
public struct Foo
{
private readonly int x;
public Foo(int x)
{
this.x = x;
}
public override string ToString()
{
return string.Format("Foo {{x={0}}}", x);
}
public override int GetHashCode()
{
return x.GetHashCode();
}
public override bool Equals(Object obj)
{
return x.Equals(obj);
}
public static bool operator ==(Foo a, Foo b)
{
return a.x == b.x;
}
public static bool operator !=(Foo a, Foo b)
{
return a.x != b.x;
}
[Obsolete("The result of the expression is always ''false'' since a value of type ''Foo'' is never equal to ''null''", true)]
public static bool operator ==(Foo a, Foo? b)
{
return false;
}
[Obsolete("The result of the expression is always ''true'' since a value of type ''Foo'' is never equal to ''null''", true)]
public static bool operator !=(Foo a, Foo? b)
{
return true;
}
[Obsolete("The result of the expression is always ''false'' since a value of type ''Foo'' is never equal to ''null''", true)]
public static bool operator ==(Foo? a, Foo b)
{
return false;
}
[Obsolete("The result of the expression is always ''true'' since a value of type ''Foo'' is never equal to ''null''", true)]
public static bool operator !=(Foo? a, Foo b)
{
return true;
}
}