¿Qué se prefiere: Nullable . HasValue o Nullable ! = Null?

Siempre utilicé (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 utilizaron (b) Nullable != null exclusivamente en su lugar. ¿Hay alguna razón para usar una sobre la otra, o es puramente preferencia?

(un)

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

(segundo)

 int? b; if (b != null) ... 

El comstackdor reemplaza las comparaciones nulas con una llamada a HasValue , por lo que no hay diferencia real. Simplemente haz lo que sea más legible / tenga más sentido para ti y tus colegas.

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

Investigué esto utilizando 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 o la taquigrafía something? es una estructura para la cual el comstackdor parece estar haciendo mucho trabajo para permitirnos usar con nulo como si fuera una clase.
Como verá a continuación, SomeNullable == null y SomeNullable.HasValue siempre devolverá un verdadero o falso esperado. Aunque no se muestra a continuación, SomeNullable == 3 es válido (suponiendo que SomeNullable es un int? ).
Mientras que SomeNullable.Value nos da un error en tiempo de ejecución si asignamos null a SomeNullable . Este es, de hecho, el único caso en el que Nullables podría causarnos un problema, gracias a una combinación de operadores sobrecargados, el método object.Equals(obj) sobrecargado, y la optimización del comstackdor y el negocio de los monos.

Aquí hay una descripción de algún código que ejecuté, y qué salida produjo en las tags:

 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, intentemos con 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 lo mismo que antes. Tenga en cuenta que la inicialización con int? val = new int?(null); int? val = new int?(null); , con nulo pasado al constructor, habría producido un error de tiempo COMPIL, ya que el VALOR del objeto que se puede nulos no puede contener nulos. Solo el objeto contenedor puede ser nulo.

Del mismo modo, obtendríamos un error de tiempo de comstackció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 una vez más, los operadores de conversión implícita sobrecargados polimorfos nos permiten:

 val = 3; 

Sin embargo, no hay necesidad de preocuparse por nada, pero si funciona bien, siempre y cuando funcione. 🙂

En VB.Net. NO use “IsNot Nothing” cuando puede usar “.HasValue”. Acabo de resolver una “Operación podría desestabilizar el tiempo de ejecución” Error de confianza medio al reemplazar “No es nada” con “.HasValue” En un solo lugar. Realmente no entiendo por qué, pero algo está sucediendo de manera diferente en el comstackdor. Supongo que “! = Null” en C # puede tener el mismo problema.

Si usa linq y quiere mantener su código corto, le recomiendo que siempre use !=null

Y esta es la razón por la cual:

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

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

Si en alguna parte de nuestro código queremos obtener todos los Foo con valores no nulos de SomeDouble de una colección de Foo (asumiendo que algunos foos en la colección también pueden ser nulos), terminamos con al menos tres formas de escribir nuestra función (si use C # 6):

 public IEnumerable GetNonNullFoosWithSomeDoubleValues(IEnumerable 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 el más corto

Respuesta general y regla de oro: si tiene una opción (por ejemplo, escribir serializadores personalizados) para procesar el valor Nullable en una canalización diferente de un object , y usar sus propiedades específicas, hágalo y use propiedades específicas anulables. Por lo tanto, desde el punto de vista del pensamiento constante HasValue debe ser preferido. El pensamiento constante puede ayudarte a escribir un mejor código, no pases demasiado tiempo en los detalles. Por ejemplo, el segundo método será muchas veces más efectivo (principalmente debido a que los comstackdores están en línea y al boxeo, pero los números son muy expresivos):

 public static bool CheckObjectImpl(object o) { return o != null; } public static bool CheckNullableImpl(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? 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 fue utilizado

PD . La gente dice que el consejo “prefiere HasValue por el pensamiento constante” no está relacionado y es inútil. ¿Puedes predecir el rendimiento de esto?

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

PPS Las personas continúan con un error pero nadie intenta predecir el rendimiento de CheckNullableGenericImpl . Y el comstackdor no lo ayudará a reemplazar !=null HasValue con HasValue . HasValue debe usarse directamente.