Siempre solía Nullable<>.HasValueporque me gustaba la semántica. Sin embargo, recientemente estaba trabajando en la base de código existente de otra persona donde la usaban Nullable<> != nullexclusivamente.
¿Hay alguna razón para usar uno sobre el otro, o es puramente preferencial?
int? a; if (a.HasValue) // ...
vs.
int? b; if (b != null) // ...

HasValueya que creo que las palabras tienden a ser más legibles que los símbolos. Sin embargo, todo depende de ti y de lo que se ajuste a tu estilo actual..HasValuetiene más sentido ya que denota que el tipo es de tipo enT?lugar de un tipo que puede ser anulable, como cadenas de caracteres.Respuestas:
El compilador reemplaza las comparaciones nulas con una llamada a
HasValue, por lo que no hay una diferencia real. Simplemente haga lo que sea más legible / tenga más sentido para usted y sus colegas.fuente
int? x = nullme da la ilusión de que una instancia anulable es un tipo de referencia. Pero la verdad es que Nullable <T> es un tipo de valor. Se siente que tendría un NullReferenceException hacer:int? x = null; Use(x.HasValue).Nullable<int>lugar deint?.Prefiero
(a != null)que la sintaxis coincida con los tipos de referencia.fuente
Nullable<>que no es un tipo de referencia.HasValueporque es más legible que!= nullInvestigué un poco sobre esto mediante el uso de diferentes métodos para asignar valores a un int anulable. Esto es lo que sucedió cuando hice varias cosas. Debería aclarar lo que está pasando. Tenga en cuenta:
Nullable<something>o la taquigrafíasomething?es una estructura para la cual el compilador parece estar haciendo mucho trabajo para permitirnos usar con nulo como si fuera una clase.Como verá a continuación,
SomeNullable == nullySomeNullable.HasValuesiempre devolverá un verdadero o falso esperado. Aunque no se muestra a continuación, tambiénSomeNullable == 3es válido (suponiendo que SomeNullable es unint?).Aunque
SomeNullable.Valueno nos lleva a un error de ejecución, si asignamosnullaSomeNullable. De hecho, este es el único caso en el que los valores nulables pueden causarnos un problema, gracias a una combinación de operadores sobrecargados, sobrecargadosobject.Equals(obj)método y optimización del compilador y negocios de monos.Aquí hay una descripción de algún código que ejecuté y qué salida produjo en las etiquetas:
Ok, intentemos el siguiente método de inicialización:
Todo lo mismo que antes. Tenga en cuenta que la inicialización 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. Es solo el objeto contenedor lo que puede ser igual a nulo.Del mismo modo, obtendríamos un error de tiempo de compilación de:
sin mencionar que
val.Valuees una propiedad de solo lectura de todos modos, lo que significa que ni siquiera podemos usar algo como:pero de nuevo, los operadores de conversión implícita sobrecargados polimorfos nos permiten hacer:
Sin embargo, no es necesario preocuparse por lo polis que puede hacer, siempre y cuando funcione correctamente. :)
fuente
Nullable<X>VisualStudio 2013 y F12, verá que solo sobrecarga la conversión hacia y desdeX, y elEquals(object other)método. Sin embargo, creo que el operador == usa ese método por defecto, por lo que el efecto es el mismo. De hecho, he tenido la intención de actualizar esta respuesta sobre ese hecho desde hace un tiempo, pero soy vago y / u ocupado. Este comentario tendrá que hacerlo por ahora :)int? val = 42; val.GetType() == typeof(int)). Entonces, no solo es anulable una estructura que puede ser igual a nula, sino que a menudo tampoco es anulable en absoluto. : D De la misma manera, cuando encajona un valor anulable, está boxeandoint, noint?, y cuandoint?no tiene un valor, obtiene ennulllugar de un valor anulable en caja. Básicamente significa que rara vez hay gastos generales por usar nullable correctamente :)Nullun tipo en .NET? ¿Puede señalar la parte de la especificación CLR / C # donde se dice eso? Los valores nulables están bien definidos en la especificación CLR, su comportamiento no es "implementación de una abstracción", es un contrato . Pero si lo mejor que puedes hacer es atacar ad hominem, diviértete.En VB.Net. NO use "IsNot Nothing" cuando pueda usar ".HasValue". Acabo de resolver un error de confianza medio "Operación podría desestabilizar el tiempo de ejecución" reemplazando "IsNot Nothing" por ".HasValue" en un solo lugar. Realmente no entiendo por qué, pero algo está sucediendo de manera diferente en el compilador. Supongo que "! = Null" en C # puede tener el mismo problema.
fuente
HasValuedebido a la legibilidad.IsNot NothingEs realmente una expresión fea (debido a la doble negación).Si usa linq y quiere mantener su código corto, recomiendo usar siempre
!=nullY esta es la razón:
Imaginemos que tenemos alguna clase
Foocon una variable doble anulableSomeDoubleSi en algún lugar de nuestro código queremos obtener todos los Foo con valores SomeDouble no nulos de una colección de Foo (suponiendo que algunos foos en la colección también puedan ser nulos), terminamos con al menos tres formas de escribir nuestra función (si use C # 6):
Y en este tipo de situación recomiendo ir siempre por la más corta
fuente
foo?.SomeDouble.HasValuees un error en tiempo de compilación (no un "lanzamiento" en mi terminología) en ese contexto porque su tipo esbool?, no solobool. (El.Wheremétodo quiere aFunc<Foo, bool>.) Está permitido hacerlo(foo?.SomeDouble).HasValue, por supuesto, ya que tiene tipobool. Esto es lo que su primera línea es "traducida" internamente por el compilador de C # (al menos formalmente).Respuesta general y regla general: si tiene una opción (por ejemplo, escribir serializadores personalizados) para procesar Nullable en una canalización diferente que
object, y usar sus propiedades específicas, hágalo y use propiedades específicas de Nullable. Por lo tanto, desde un punto de vista de pensamiento consistente,HasValuedebería preferirse. El pensamiento consistente puede ayudarlo a escribir un mejor código sin perder demasiado tiempo en detalles. Por ejemplo, el segundo método será muchas veces más efectivo (principalmente debido a los compiladores en línea y boxeo, pero aún así los números son muy expresivos):Prueba de referencia:
Código de referencia:
https://github.com/dotnet/BenchmarkDotNet fue utilizado
PS . La gente dice que el consejo "prefiere HasValue debido a un pensamiento constante" no está relacionado e inútil. ¿Puedes predecir el rendimiento de esto?
PPS Las personas continúan negativas pero nadie intenta predecir el rendimiento de
CheckNullableGenericImpl. Y hay compilador no le ayudará a reemplazar!=nullconHasValue.HasValuedebe usarse directamente si está interesado en el rendimiento.fuente
CheckObjectImplcajas se anulan en unobject, mientrasCheckNullableImplque no utiliza el boxeo. Por lo tanto, la comparación es muy inquieta. No solo no es una tarifa, sino que también es inútil porque, como se señaló en la respuesta aceptada , el compilador reescribe!=deHasValuetodos modos.Nullable<T>, tú sí (al encajonarlo en unobject). Cuando se aplica!= nullcon un nulable a la izquierda, no se produce el boxeo porque el soporte!=para nulables funciona en el nivel del compilador. Es diferente cuando ocultas el anulable del compilador al encajonarlo primero en unobject. NiCheckObjectImpl(object o)su punto de referencia tienen sentido en principio.CheckObjectImplcon su cuerpo en el interiorCheckObject. Sin embargo, sus últimos comentarios revelan que, de hecho, tenía una pregunta completamente diferente en mente cuando decidió responder a esta pregunta de 8 años, lo que hace que su respuesta sea engañosa en el contexto de la pregunta original. No era lo que estaba preguntando el OP.what is faster != or HasValue. Llega a esta pregunta, examina su respuesta, aprecia su punto de referencia y dice: "¡Dios, nunca lo usaré!=porque claramente es mucho más lento!" Esa es una conclusión muy errónea que luego procederá a difundir. Es por eso que creo que su respuesta es dañina: responde una pregunta incorrecta y, por lo tanto, planta una conclusión incorrecta en el lector desprevenido. Considere lo que sucede cuando cambia deCheckNullableImpla también serreturn o != null;Usted obtendrá el mismo resultado referencia.!=yHasValuecuando, de hecho, muestra la diferencia entreobject oyT? o. Si haces lo que te sugerí, es decir, reescribirCheckNullableImplcomopublic static bool CheckNullableImpl<T>(T? o) where T: struct { return o != null; }, terminarás con un punto de referencia que muestra claramente que!=es mucho más lento que!=. Lo que debería llevarlo a la conclusión de que el problema que describe su respuesta no se trata en absoluto de!=vs.HasValue