Co jest preferowane: Nullable<>.HasValue lub Nullable<>!= null?
Zawsze używałem (a) Nullable<>.HasValue
ponieważ lubiłem semantykę. Jednak ostatnio pracowałem nad istniejącą bazą kodu innej osoby, gdzie używali wyłącznie (b) Nullable<> != null
. Czy jest powód, aby używać jednego nad drugim, czy jest to czysto preferencyjne?
(a)
int? a;
if (a.HasValue)
...
(b)
int? b;
if (b != null)
...
6 answers
Kompilator zastępuje porównania null wywołaniem HasValue
, więc nie ma rzeczywistej różnicy. Po prostu zrób to, co jest bardziej czytelne/ma większy sens dla Ciebie i twoich kolegów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-24 03:34:09
Wolę (a != null)
, aby składnia pasowała do typów referencji.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-30 09:16:28
Zrobiłem kilka badań na ten temat, używając różnych metod, aby przypisać wartości do nullable int. Oto, co się stało, gdy robiłem różne rzeczy. Powinno wyjaśnić, o co chodzi.
Należy pamiętać: {[5] } lub skrót {[6] } jest strukturą, dla której kompilator wydaje się wykonywać wiele pracy, aby pozwolić nam używać z null tak, jakby była klasą.
Jak zobaczysz poniżej, SomeNullable == null
i SomeNullable.HasValue
zawsze zwrócą oczekiwaną wartość true lub false. Chociaż nie wykazano poniżej, SomeNullable == 3
jest również poprawne (zakładając SomeNullable to int?
).
Podczas gdy SomeNullable.Value
wyświetla nam błąd runtime, jeśli przypisaliśmy null
do SomeNullable
. W rzeczywistości jest to jedyny przypadek, w którym nullables może sprawić nam problem, dzięki kombinacji przeciążonych operatorów, przeciążonej metody object.Equals(obj)
oraz optymalizacji kompilatora i monkey business.
Oto opis kodu, który uruchomiłem, i jakie wyjście wyprodukował w etykietach:
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, spróbujmy następnej metody inicjalizacji:
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.")
Wszystkie takie same jak wcześniej. Należy pamiętać, że inicjalizacja za pomocą int? val = new int?(null);
, z null przekazanym konstruktorowi, spowodowałaby błąd w czasie kompilacji, ponieważ wartość obiektu nullable nie jest nullable. Tylko sam obiekt wrapper może być równy null.
Podobnie, otrzymamy błąd czasu kompilacji z:
int? val = new int?();
val.Value = null;
Nie wspominając o tym, że val.Value
jest właściwością tylko do odczytu, co oznacza, że nie możemy nawet użyć czegoś takiego jak:
val.Value = 3;
/ Align = "left" / operatory let us do:
val = 3;
Nie musisz się martwić o polisomthing cochamacallits choć, tak długo, jak to działa dobrze? :)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-25 21:09:37
In VB.Net. nie używaj "IsNot Nothing", kiedy możesz użyć".HasValue". Właśnie rozwiązałem "operacja może zdestabilizować błąd średniego zaufania runtime", zastępując "IsNot Nothing "przez".HasValue " w jednym miejscu. Nie bardzo rozumiem dlaczego, ale coś dzieje się inaczej w kompilatorze. Przypuszczam, że"!= null " W C# może mieć ten sam problem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-23 18:13:55
Jeśli używasz linq i chcesz zachować krótki kod, polecam zawsze używać !=null
I dlatego:
Wyobraźmy sobie, że mamy jakąś klasę Foo
z nullable double zmienną SomeDouble
public class Foo
{
public double? SomeDouble;
//some other properties
}
Jeśli gdzieś w naszym kodzie chcemy uzyskać wszystkie Foo z inną niż null wartościami z kolekcji Foo (zakładając, że niektóre Foo w kolekcji też mogą być null), otrzymujemy co najmniej trzy sposoby zapisu naszej funkcji (jeśli używamy 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
}
I w tego typu sytuacji polecam zawsze iść na krótszy
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-09 08:18:00
Ogólna odpowiedź i zasada: jeśli masz opcję (np. pisanie niestandardowych serializerów), aby przetwarzać Nullable w innym potoku niż object
- i używać ich specyficznych właściwości - zrób to i użyj specyficznych właściwości Nullable.
Więc z konsekwentnego myślenia punktu widzenia HasValue
powinny być preferowane. Konsekwentne myślenie może pomóc ci napisać lepszy kod nie spędzając zbyt wiele czasu w szczegółach.
Np. druga metoda będzie wielokrotnie skuteczniejsza (głównie ze względu na wbudowane Kompilatory i boks, ale i tak liczby są bardzo wyraziste):
public static bool CheckObjectImpl(object o)
{
return o != null;
}
public static bool CheckNullableImpl<T>(T? o) where T: struct
{
return o.HasValue;
}
Test porównawczy:
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 |
Benchmark code:
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 został użyty
PS . Ludzie mówią, że porady "preferują HasValue z powodu konsekwentnego myślenia" nie są powiązane i bezużyteczne. możesz przewidzieć wydajność tego?
public static bool CheckNullableGenericImpl<T>(T? t) where T: struct
{
return t != null;
}
PPS ludzie nadal minus, ale nikt nie próbuje przewidzieć wydajności CheckNullableGenericImpl
. I tam kompilator nie pomoże Ci zastąpić !=null
przez HasValue
. HasValue
należy stosować bezpośrednio.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-21 11:29:14