Pregunta

siempre usé Nullable<>.HasValue porque me gustó la semántica.Sin embargo, recientemente estuve trabajando en el código base existente de otra persona donde usaban Nullable<> != null exclusivamente en su lugar.

¿Hay alguna razón para utilizar uno sobre el otro o es pura preferencia?

  1. int? a;
    if (a.HasValue)
        // ...
    

vs.

  1. int? b;
    if (b != null)
        // ...
    
¿Fue útil?

Solución

El compilador reemplaza comparaciones nulos con una llamada a HasValue, por lo que no hay ninguna diferencia real. Sólo hacer que sea más fácil de leer / tiene más sentido para usted y sus colegas.

Otros consejos

Yo prefiero (a != null) de manera que coincida con la sintaxis de los tipos de referencia.

Investigué un poco sobre esto usando diferentes métodos para asignar valores a un int que acepta valores NULL.Esto es lo que pasó cuando hice varias cosas.Debería aclarar lo que está pasando.Tenga en cuenta: Nullable<something> o la taquigrafía something? Es una estructura para la cual el compilador parece estar trabajando mucho para permitirnos usar null como si fuera una clase.
Como verás a continuación, SomeNullable == null y SomeNullable.HasValue siempre devolverá un valor esperado verdadero o falso.Aunque no se demuestra a continuación, SomeNullable == 3 también es válido (asumiendo que SomeNullable es un int?).
Mientras SomeNullable.Value nos da un error de tiempo de ejecución si asignamos null a SomeNullable.De hecho, este es el único caso en el que los valores nulos podrían causarnos un problema, gracias a una combinación de operadores sobrecargados, sobrecargados object.Equals(obj) método, y optimización del compilador y negocios monótonos.

Aquí hay una descripción de parte del código que ejecuté y el resultado que produjo en las etiquetas:

int? val = null;
lbl_Val.Text = val.ToString(); //Produced an empty string.
lbl_ValVal.Text = val.Value.ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValEqNull.Text = (val == null).ToString(); //Produced "True" (without the quotes)
lbl_ValNEqNull.Text = (val != null).ToString(); //Produced "False"
lbl_ValHasVal.Text = val.HasValue.ToString(); //Produced "False"
lbl_NValHasVal.Text = (!(val.HasValue)).ToString(); //Produced "True"
lbl_ValValEqNull.Text = (val.Value == null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValValNEqNull.Text = (val.Value != null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")

Bien, probemos el siguiente método de inicialización:

int? val = new int?();
lbl_Val.Text = val.ToString(); //Produced an empty string.
lbl_ValVal.Text = val.Value.ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValEqNull.Text = (val == null).ToString(); //Produced "True" (without the quotes)
lbl_ValNEqNull.Text = (val != null).ToString(); //Produced "False"
lbl_ValHasVal.Text = val.HasValue.ToString(); //Produced "False"
lbl_NValHasVal.Text = (!(val.HasValue)).ToString(); //Produced "True"
lbl_ValValEqNull.Text = (val.Value == null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")
lbl_ValValNEqNull.Text = (val.Value != null).ToString(); //Produced a runtime error. ("Nullable object must have a value.")

Todo igual que antes.Tenga en cuenta que inicializar con int? val = new int?(null);, con nulo pasado al constructor, habría producido un error de tiempo de COMPILACIÓN, ya que el VALOR del objeto anulable NO es anulable.Sólo el objeto contenedor en sí puede ser igual a nulo.

Del mismo modo, obtendríamos un error en tiempo de compilación de:

int? val = new int?();
val.Value = null;

sin mencionar eso val.Value De todos modos, es una propiedad de solo lectura, lo que significa que ni siquiera podemos usar algo como:

val.Value = 3;

pero nuevamente, los operadores de conversión implícitos sobrecargados polimorfos nos permiten hacer:

val = 3;

Sin embargo, no hay necesidad de preocuparse por algo polivalente, como se llame, siempre y cuando funcione correctamente.:)

En VB.Net. NO utilice "IsNot Nada" cuando se puede usar ".HasValue". Acabo de resolver una "operación podría desestabilizar el tiempo de ejecución" error de confianza medio mediante la sustitución de "IsNot Nada" con ".HasValue" en un solo lugar. Realmente no entiendo por qué, pero algo está ocurriendo de manera diferente en el compilador. Yo asumiría que "! = Null" en C # pueden tener el mismo problema.

Si utiliza LINQ y desea mantener su código corto, yo recomiendo usar siempre !=null

Y es por esto:

Vamos a imaginar que tenemos alguna clase Foo con un anulable doble Variable SomeDouble

public class Foo
{
    public double? SomeDouble;
    //some other properties
}   

Si en algún lugar de nuestro código que queremos obtener toda Foo con un no nulos valores SomeDouble de una colección de Foo (asumiendo algunas Foos de la colección puede ser nulo también), nos encontramos con al menos tres vías para escribir nuestra función (si se utiliza C # 6):

public IEnumerable<Foo> GetNonNullFoosWithSomeDoubleValues(IEnumerable<Foo> foos)
{
     return foos.Where(foo => foo?.SomeDouble != null);
     return foos.Where(foo=>foo?.SomeDouble.HasValue); // compile time error
     return foos.Where(foo=>foo?.SomeDouble.HasValue == true); 
     return foos.Where(foo=>foo != null && foo.SomeDouble.HasValue); //if we don't use C#6
}

Y en este tipo de situación Recomiendo ir siempre para el más corto

respuesta general y regla de oro: si usted tiene una opción (por ejemplo, escribiendo serializadores personalizados) para procesar anulable en diferentes tuberías que object - y utilizar sus propiedades específicas - hacerlo y utilizar las propiedades específicas anulables. Por lo tanto, desde el punto de vista pensamiento coherente HasValue se debe preferir. pensamiento coherente puede ayudar a escribir mejor código no pasar demasiado tiempo en detalles. P.ej. hay segundo método será muchas veces más eficaz (sobre todo debido a los compiladores de procesos en línea y el boxeo, pero todavía los números son muy expresivos):

public static bool CheckObjectImpl(object o)
{
    return o != null;
}

public static bool CheckNullableImpl<T>(T? o) where T: struct
{
    return o.HasValue;
}

Prueba de referencia:

BenchmarkDotNet=v0.10.5, OS=Windows 10.0.14393
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233539 Hz, Resolution=309.2587 ns, Timer=TSC
  [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1648.0
  Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1648.0
  Core   : .NET Core 4.6.25009.03, 64bit RyuJIT


        Method |  Job | Runtime |       Mean |     Error |    StdDev |        Min |        Max |     Median | Rank |  Gen 0 | Allocated |
-------------- |----- |-------- |-----------:|----------:|----------:|-----------:|-----------:|-----------:|-----:|-------:|----------:|
   CheckObject |  Clr |     Clr | 80.6416 ns | 1.1983 ns | 1.0622 ns | 79.5528 ns | 83.0417 ns | 80.1797 ns |    3 | 0.0060 |      24 B |
 CheckNullable |  Clr |     Clr |  0.0029 ns | 0.0088 ns | 0.0082 ns |  0.0000 ns |  0.0315 ns |  0.0000 ns |    1 |      - |       0 B |
   CheckObject | Core |    Core | 77.2614 ns | 0.5703 ns | 0.4763 ns | 76.4205 ns | 77.9400 ns | 77.3586 ns |    2 | 0.0060 |      24 B |
 CheckNullable | Core |    Core |  0.0007 ns | 0.0021 ns | 0.0016 ns |  0.0000 ns |  0.0054 ns |  0.0000 ns |    1 |      - |       0 B |

Código de referencia:

public class BenchmarkNullableCheck
{
    static int? x = (new Random()).Next();

    public static bool CheckObjectImpl(object o)
    {
        return o != null;
    }

    public static bool CheckNullableImpl<T>(T? o) where T: struct
    {
        return o.HasValue;
    }

    [Benchmark]
    public bool CheckObject()
    {
        return CheckObjectImpl(x);
    }

    [Benchmark]
    public bool CheckNullable()
    {
        return CheckNullableImpl(x);
    }
}

https://github.com/dotnet/BenchmarkDotNet se utilizó

PS . La gente dice que el consejo "prefieren HasValue debido pensamiento coherente" no está relacionado e inútil. ¿Se puede predecir el rendimiento de esto?

public static bool CheckNullableGenericImpl<T>(T? t) where T: struct
{
    return t != null;
}

PPS Las personas continúan menos pero nadie trata de predecir el rendimiento de CheckNullableGenericImpl. Y hay compilador no le ayudará a reemplazar !=null con <=>. <=> deben utilizarse directamente si está interesado en el rendimiento.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top