C# Nullable<;T>;。HasValue或Nullable<;T>;!=无效的
我总是使用C# Nullable<;T>;。HasValue或Nullable<;T>;!=无效的,c#,.net,null,nullable,C#,.net,Null,Nullable,我总是使用Nullable.HasValue,因为我喜欢它的语义。然而,最近我正在处理其他人的现有代码库,他们使用了Nullable!=空而不是独占 是否有理由使用其中一个而不是另一个,或者这纯粹是偏好 vs 编译器用调用HasValue来替换空比较,因此没有真正的区别。只要做对您和您的同事来说更可读/更有意义的事情。我更喜欢(a!=null),以便语法与VB.Net中的引用类型匹配。。当您可以使用“.HasValue”时,不要使用“IsNotNothing”。我刚刚解决了一个“操作可能会破
Nullable.HasValue
,因为我喜欢它的语义。然而,最近我正在处理其他人的现有代码库,他们使用了Nullable!=空
而不是独占
是否有理由使用其中一个而不是另一个,或者这纯粹是偏好
vs
编译器用调用
HasValue
来替换空比较,因此没有真正的区别。只要做对您和您的同事来说更可读/更有意义的事情。我更喜欢(a!=null)
,以便语法与VB.Net中的引用类型匹配。。当您可以使用“.HasValue”时,不要使用“IsNotNothing”。我刚刚解决了一个“操作可能会破坏运行时稳定性”中的信任错误,在一个位置用“.HasValue”替换了“isnothing”。我真的不明白为什么,但是编译器中发生了一些不同的事情。我假设C#中的“!=null”可能也有同样的问题。我对此做了一些研究,使用不同的方法将值赋给可为null的int。下面是我做各种事情时发生的情况。应该澄清发生了什么。
请记住:Nullable
或缩写something?
是一个结构,编译器似乎在为它做大量工作,让我们像使用类一样使用null。如下所示,
SomeNullable==null
和SomeNullable.HasValue
将始终返回预期的true或false。尽管下面没有演示,SomeNullable==3
也是有效的(假设SomeNullable是int?
)。如果将
SomeNullable.Value
分配给SomeNullable
,则SomeNullable.Value
会导致运行时错误。事实上,这是唯一一个nullables会给我们带来问题的情况,这要感谢重载运算符、重载的object.Equals(obj)
方法、编译器优化和monkey business的组合
下面是我运行的一些代码的描述,以及它在标签中产生的输出:
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=新整数?(空)将null传递给构造函数的代码>,将产生编译时错误,因为可为null对象的值不可为null。只有包装器对象本身可以等于null
类似地,我们将从以下位置获得编译时错误:
int? val = new int?();
val.Value = null;
更不用说val.Value
是一个只读属性,这意味着我们甚至不能使用以下内容:
val.Value = 3;
但同样,多形重载隐式转换运算符允许我们:
val = 3;
不必担心多义词,但只要它能正常工作,它又叫什么呢?:) 第二种方法的效率要高得多(主要是因为编译器内联和装箱,但数字仍然非常有表现力):
基准代码:
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);
}
}
PPS人们继续减号,似乎没有人试图预测CheckNullableGenericImpl
的性能。我会告诉你:编译器不会帮你替换=空值
和HasValue
<如果您对性能感兴趣,应该直接使用code>HasValue
。如果您使用linq并希望代码简短,我建议您始终使用=空
这就是为什么:
假设我们有一个类Foo
,它有一个可为空的double变量SomeDouble
public class Foo
{
public double? SomeDouble;
//some other properties
}
如果在代码中的某个地方,我们希望从一个Foo集合中获取所有具有非null SomeDouble值的Foo(假设集合中的一些Foo也可以为null),那么我们至少有三种方法来编写我们的函数(如果我们使用C#6):
public IEnumerable GetNonNullFoosWithSomeDoubleValue(IEnumerable foos)
{
返回foos.Where(foo=>foo?.SomeDouble!=null);
返回foos.Where(foo=>foo?.SomeDouble.HasValue);//编译时错误
返回foos.Where(foo=>foo?.SomeDouble.HasValue==true);
返回foos.Where(foo=>foo!=null&&foo.SomeDouble.HasValue);//如果我们不使用C#6
}
在这种情况下,我建议总是选择较短的一个我问了一个类似的问题。。。得到了一些很好的答案:就个人而言,我会使用
HasValue
,因为我认为文字比符号更容易阅读。不过,这取决于您,以及什么适合您现有的样式。.HasValue
更有意义,因为它表示类型是T?
类型,而不是可以为null的类型,例如字符串。我想补充一点,“更一致的类型/遵循现有的编码样式。”哇。我讨厌这种糖<代码>整数?x=null让我产生一种错觉,认为可为null的实例是引用类型。但事实是Nullable是一种值类型。感觉我将得到一个NullReferenceException来执行:int?x=零;使用(x.HasValue)
@KFL如果语法上的糖分让你感到困扰,就使用Nullable
而不是int?
。在创建应用程序的早期阶段,你可能认为使用一个Nullable value类型来存储一些数据就足够了,但过了一段时间,你才意识到你需要一个适合自己的类。编写了与null比较的原始代码,这样做的好处是,您不需要用null比较来搜索/替换对HasValue()的每个调用。抱怨能够将null设置为null或将其与null进行比较是非常愚蠢的,因为这被称为null。问题是人们把“引用类型”和“可以为空”混为一谈,但这是概念上的混乱。未来的C#将具有不可为空的引用类型。由于可读性,我更喜欢HasValue
isnothing
确实是一个丑陋的表达(因为双重否定)。@steffan“isnothing”不是双重否定。“没什么”不是否定的
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);
}
}
public static bool CheckNullableGenericImpl<T>(T? t) where T: struct
{
return t != null; // or t.HasValue?
}
public class Foo
{
public double? SomeDouble;
//some other properties
}
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
}