Que se prefiere: Nullable<>.HasValue o Nullable<>!= null?


Siempre usé (a) Nullable<>.HasValue porque me gustaba la semántica. Sin embargo, recientemente estaba trabajando en la base de código existente de otra persona donde usaron (b) Nullable<> != null exclusivamente en su lugar. ¿Hay alguna razón para usar uno sobre el otro, o es puramente preferencia?

A)

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

(b)

int? b;
if (b != null)
    ...
Author: Palec, 2009-03-24

6 answers

El compilador reemplaza las comparaciones nulas con una llamada a HasValue, por lo que no hay diferencia real. Simplemente haga lo que sea más legible/tenga más sentido para usted y sus colegas.

 382
Author: Rex M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-03-24 03:34:09

Prefiero (a != null) para que la sintaxis coincida con los tipos de referencia.

 41
Author: cbp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-30 09:16:28

Hice algunas investigaciones sobre esto mediante el uso de diferentes métodos para asignar valores a un int nullable. Esto es lo que sucedió cuando hice varias cosas. Debería aclarar lo que está pasando. Tenga en cuenta: Nullable<something> o la abreviatura something? es una estructura para la que el compilador parece estar haciendo mucho trabajo para permitirnos usar con null como si fuera una clase.
Como verás a continuación, SomeNullable == null y SomeNullable.HasValue siempre devolverán un verdadero o falso esperado. Aunque no se demuestra a continuación, SomeNullable == 3 es válido también (suponiendo SomeNullable es un int?).
While 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 las nullables podrían causarnos un problema, gracias a una combinación de operadores sobrecargados, el método object.Equals(obj) sobrecargado, y la optimización del compilador y el monkey business.

Aquí está una descripción de algún código que corrí, y qué salida 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.")

Ok, 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.")

Todos lo mismo que antes. Tenga en cuenta que inicializar con int? val = new int?(null);, con null pasado al constructor, habría producido un error de tiempo de COMPILACIÓN, ya que el VALOR del objeto nullable NO es nullable. Es solo el objeto wrapper el que puede ser igual a null.

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

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

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

val.Value = 3;

Pero de nuevo, la conversión implícita polimorfa sobrecargada operadores vamos a hacer:

val = 3;

No hay necesidad de preocuparse por polysomthing whatchamacallits sin embargo, siempre y cuando funcione bien? :)

 19
Author: Perrin Larson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-25 21:09:37

En VB.Net. NO utilice "IsNot Nada" cuando se puede utilizar".HasValue". Acabo de resolver un" 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á sucediendo de manera diferente en el compilador. Yo asumiría que"!= null " en C# puede tener el mismo problema.

 14
Author: Carter Medlin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-04-23 18:13:55

Si usas linq y quieres mantener tu código corto, recomiendo usar siempre !=null

Y esta es la razón:

Imaginemos que tenemos alguna clase Foo con una doble anulable variable SomeDouble

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

Si en algún lugar de nuestro código queremos obtener todos los Foo con un valor no nulo SomeDouble de una colección de Foo (suponiendo que algunos foos en la colección también pueden ser nulos), terminamos con al menos tres formas de escribir nuestra función (si usamos 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 por la más corta

 1
Author: yan yankelevich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-09 08:18:00

Respuesta general y regla general: si tiene una opción (por ejemplo, escribir serializadores personalizados) para procesar Nullable en una canalización diferente a object - y usar sus propiedades específicas - hágalo y use propiedades específicas Nullable. Así que desde el punto de vista del pensamiento coherente HasValue debe ser preferido. El pensamiento consistente puede ayudarle a escribir mejor código no gastar demasiado tiempo en detalles. Por ejemplo, hay segundo método será muchas veces más eficaz (sobre todo debido a los compiladores 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 "prefiere HasValue debido a un pensamiento consistente" no está relacionado e inútil. ¿Puedes predecir el rendimiento de esto?

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

PPS La gente continúa menos pero nadie intenta predecir el rendimiento de CheckNullableGenericImpl. Y no compilador le ayudará a reemplazar !=null con HasValue. HasValue debe utilizarse directamente.

 -5
Author: Roman Pokrovskij,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-21 11:29:14