哪个是首选的:可为空<>。HasValue或Nullable <>!= null?
我总是使用(a) Nullable<>.HasValue
因为我喜欢语义。 但是,最近我正在使用别人现有的代码库(b) Nullable<> != null
。 是有理由相互使用,还是纯粹偏好?
(一个)
int? a; if (a.HasValue) ...
(b)中
int? b; if (b != null) ...
编译器用HasValue
调用replace空比较,所以没有实际的区别。 只要做到可读性更高或对您和您的同事更有意义。
我更喜欢(a != null)
以便语法匹配引用types。
我做了一些研究,通过使用不同的方法将值赋给一个可为空的int。 当我做了各种各样的事情时,发生了什么。 应该澄清发生了什么事情。 记住: Nullable<something>
或简写的something?
是一个结构,编译器似乎在做很多工作,让我们使用null,就像它是一个类一样。
如下所示, SomeNullable == null
和SomeNullable.HasValue
将始终返回预期的true或false。 尽pipe下面没有演示, 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);
初始化int? val = new int?(null);
int? val = new int?(null);
,将null传递给构造函数,会产生一个COMPILE时间错误,因为可空对象的VALUE不可为空。 只有包装对象本身可以等于null。
同样,我们将得到一个编译时错误:
int? val = new int?(); val.Value = null;
更不用说val.Value
是一个只读的属性,意味着我们甚至不能使用像这样的东西:
val.Value = 3;
但同样,多态重载隐式转换运算符让我们做:
val = 3;
不需要担心多媒体的东西,只要它的工作正确吗? 🙂
在VB.Net。 当您使用“.HasValue”时,不要使用“IsNot Nothing”。 我刚刚解决了“操作可能会破坏运行时”中等信任错误,用“.HasValue”replace“IsNot Nothing”在一个地方。 我不明白为什么,但编译器中发生了不同的事情。 我会假设C#中的“!= null”可能有相同的问题。
如果你使用linq并且希望保持你的代码简短,我build议总是使用!=null
这就是为什么:
假设我们有一个带有可变双variablesSomeDouble
类Foo
public class Foo { public double? SomeDouble; //some other properties }
如果我们的代码中的某个地方,我们想要从Foo集合中获得一个非null的SomeDouble值(假设集合中的某些foos也可以为null),我们最终会用至less三种方式来编写我们的函数(如果我们使用C#6):
public IEnumerable<Foo> GetNonNullFoosWithSomeDoubleValues(IEnumerable<Foo> foos) { return foos.Where(foo => foo?.SomeDouble != null); return foos.Where(foo=>foo?.SomeDouble.HasValue); // will throw an 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 }
在这种情况下,我build议总是select较短的一个
一般的回答和经验法则:如果你有一个选项(例如编写自定义序列化器)在不同于object
pipe道中处理Nullable,并且使用它们的特定属性,那么执行它并使用Nullable特定的属性。 所以从一致的思想来看, HasValue
应该是首选。 一致的思维能够帮助你写出更好的代码,不会花费太多的时间在细节上。 例如,第二种方法会更有效(主要是因为编译器内联和装箱,但仍然是数字非常有performance力):
public static bool CheckObjectImpl(object o) { return o != null; } public static bool CheckNullableImpl<T>(T? o) where T: struct { return o.HasValue; }
基准testing:
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人们说,这不相关和无用。 你能预测这个performance吗:
public static bool CheckNullableGenericImpl<T>(T? t) where T: struct { return t != null; }