Siempre solí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 la usaban Nullable<> != null
exclusivamente.
¿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) // ...
HasValue
ya 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..HasValue
tiene 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 = null
me 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.HasValue
porque es más legible que!= null
Investigué 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 == null
ySomeNullable.HasValue
siempre devolverá un verdadero o falso esperado. Aunque no se muestra a continuación, tambiénSomeNullable == 3
es válido (suponiendo que SomeNullable es unint?
).Aunque
SomeNullable.Value
no nos lleva a un error de ejecución, si asignamosnull
aSomeNullable
. 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.Value
es 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 ennull
lugar de un valor anulable en caja. Básicamente significa que rara vez hay gastos generales por usar nullable correctamente :)Null
un 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
HasValue
debido a la legibilidad.IsNot Nothing
Es realmente una expresión fea (debido a la doble negación).Si usa linq y quiere mantener su código corto, recomiendo usar siempre
!=null
Y esta es la razón:
Imaginemos que tenemos alguna clase
Foo
con una variable doble anulableSomeDouble
Si 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.HasValue
es un error en tiempo de compilación (no un "lanzamiento" en mi terminología) en ese contexto porque su tipo esbool?
, no solobool
. (El.Where
mé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,HasValue
deberí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!=null
conHasValue
.HasValue
debe usarse directamente si está interesado en el rendimiento.fuente
CheckObjectImpl
cajas se anulan en unobject
, mientrasCheckNullableImpl
que 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!=
deHasValue
todos modos.Nullable<T>
, tú sí (al encajonarlo en unobject
). Cuando se aplica!= null
con 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.CheckObjectImpl
con 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 deCheckNullableImpl
a también serreturn o != null;
Usted obtendrá el mismo resultado referencia.!=
yHasValue
cuando, de hecho, muestra la diferencia entreobject o
yT? o
. Si haces lo que te sugerí, es decir, reescribirCheckNullableImpl
comopublic 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