我总是使用Nullable<>.HasValue
因为我喜欢语义。但是,最近我正在处理其他人现有的代码库,他们只使用Nullable<> != null
代替。
是否有理由使用其中一种,或者纯粹是偏好?
int? a;
if (a.HasValue)
// ...
VS
int? b;
if (b != null)
// ...
答案 0 :(得分:420)
编译器将空比较替换为对HasValue
的调用,因此没有真正的区别。只要做一个对您和您的同事更具可读性/更有意义的事情。
答案 1 :(得分:41)
我更喜欢(a != null)
,以便语法与引用类型匹配。
答案 2 :(得分:20)
我通过使用不同的方法为可归零的int赋值来做了一些研究。这是我做各种事情时发生的事情。应该澄清发生了什么。
请记住:Nullable<something>
或简写something?
是一个结构,编译器似乎正在做很多工作,让我们使用null,就好像它是一个类。
正如您将在下面看到的那样,SomeNullable == null
和SomeNullable.HasValue
将始终返回预期的真或假。虽然下面没有说明,SomeNullable == 3
也是有效的(假设SomeNullable是int?
)
如果我们将SomeNullable.Value
分配给null
,SomeNullable
会给我们带来运行时错误。事实上,这是唯一可以导致我们出现问题的情况,这要归功于重载运算符,重载object.Equals(obj)
方法,编译器优化和猴子业务的组合。
以下是我运行的一些代码的说明,以及它在标签中生成的输出:
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.")
好的,让我们尝试下一个初始化方法:
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.")
和以前一样。请记住,使用int? val = new int?(null);
初始化,并将null传递给构造函数,会产生COMPILE时间错误,因为可空对象的VALUE不可为空。只有包装器对象本身才能等于null。
同样,我们会收到编译时错误:
int? val = new int?();
val.Value = null;
更不用说val.Value
无论如何都是只读属性,这意味着我们甚至无法使用以下内容:
val.Value = 3;
但同样,多态重载隐式转换运算符让我们这样做:
val = 3;
虽然它可以正常工作,但是不需要担心多元化的问题。 :)
答案 3 :(得分:13)
在VB.Net中。当你可以使用“.HasValue”时,不要使用“IsNot Nothing”。我刚刚在一个地方用“.HasValue”替换了“IsNot Nothing”,解决了“操作可能使运行时不稳定”的中等信任错误。我真的不明白为什么,但在编译器中发生了不同的事情。我认为C#中的“!= null”可能会有同样的问题。
答案 4 :(得分:1)
如果您使用linq并希望保持代码简短,我建议始终使用!=null
这就是原因:
假设我们有一个类Foo
带有可以为空的双变量SomeDouble
public class Foo
{
public double? SomeDouble;
//some other properties
}
如果在我们的代码中的某个地方,我们希望从Foo的集合中获取所有Foo的非空SomeDouble值(假设集合中的某些foos也可以为null),我们最终至少有三种方式来编写我们的函数(如果我们使用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
}
在这种情况下,我建议总是选择较短的
答案 5 :(得分:-6)
一般答案和经验法则:如果您有一个选项(例如编写自定义序列化程序)来处理与object
不同的管道中的Nullable - 并使用它们的特定属性 - 请执行此操作并使用Nullable特定属性。
因此,从一致的思维角度来看HasValue
应该是首选。一致的思考可以帮助你编写更好的代码,不要在细节上花太多时间。
例如。有第二种方法会有多倍的效果(主要是因为编译器内联和装箱,但仍然数字很有表现力):
public static bool CheckObjectImpl(object o)
{
return o != null;
}
public static bool CheckNullableImpl<T>(T? o) where T: struct
{
return o.HasValue;
}
基准测试:
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 |
基准代码:
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
<强> PS 即可。人们说建议&#34;更喜欢HasValue,因为他们思维一致&#34;没有关系也没用。 你能预测一下它的表现吗?
public static bool CheckNullableGenericImpl<T>(T? t) where T: struct
{
return t != null;
}
PPS 人们继续减去但没有人试图预测CheckNullableGenericImpl
的效果。并且编译器无法帮助您将!=null
替换为HasValue
。如果您对性能感兴趣,应直接使用HasValue
。