我一直用 Nullable<>.HasValue 因为我喜欢它的语义。然而,最近我正在研究其他人的现有代码库,他们使用了 Nullable<> != null 完全相反。

是否有理由使用其中一种而不是另一种,或者纯粹是偏好?

  1. int? a;
    if (a.HasValue)
        // ...
    

  1. int? b;
    if (b != null)
        // ...
    
有帮助吗?

解决方案

编译器将空比较替换为调用 HasValue, ,所以没有真正的区别。只需选择对您和您的同事来说更具可读性/更有意义的方法即可。

其他提示

我更喜欢 (a != null) 以便语法与引用类型匹配。

我对此进行了一些研究,使用不同的方法将值分配给可为 null 的 int。这是我做各种事情时发生的事情。应该澄清是怎么回事。记住: Nullable<something> 或简写 something? 是一个结构体,编译器似乎做了很多工作,让我们可以像使用类一样使用 null 。
正如您将在下面看到的, SomeNullable == nullSomeNullable.HasValue 将始终返回预期的 true 或 false。虽然下面没有展示, SomeNullable == 3 也是有效的(假设 SomeNullable 是 int?).
尽管 SomeNullable.Value 如果我们分配了,就会出现运行时错误 nullSomeNullable. 。事实上,这是可空值可能给我们带来问题的唯一情况,这要归功于重载运算符的组合,重载 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 传递给构造函数,则会产生编译时错误,因为可空对象的 VALUE 不可为空。只有包装对象本身可以等于 null。

同样,我们会从以下位置得到编译时错误:

int? val = new int?();
val.Value = null;

更不用说 val.Value 无论如何,它是一个只读属性,这意味着我们甚至不能使用以下内容:

val.Value = 3;

但同样,多态重载隐式转换运算符让我们可以这样做:

val = 3;

无需担心多东西什么的,只要它能正常工作就可以了?:)

在 VB.Net 中。当您可以使用“.HasValue”时,请勿使用“IsNot Nothing”。我刚刚通过将“IsNot Nothing”替换为“.HasValue”在一处解决了“操作可能会破坏运行时稳定性”中等信任错误。我不太明白为什么,但编译器中发生了不同的事情。我假设 C# 中的“!= null”可能有同样的问题。

如果您使用 linq 并希望保持代码简短,我建议始终使用 !=null

这就是原因:

想象一下我们有一些课 Foo 与一个 可空双精度 多变的 SomeDouble

public class Foo
{
    public double? SomeDouble;
    //some other properties
}   

如果在我们的代码中的某个地方,我们想要从 Foo 集合中获取具有非 null SomeDouble 值的所有 Foo(假设集合中的某些 foo 也可以为 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
}

在这种情况下,我建议始终选择较短的

一般答案和经验法则:如果你有一个选择(例如编写自定义序列化器)以在不同的管道中处理 Nullable object - 并使用它们的特定属性 - 这样做并使用 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 被使用

聚苯乙烯. 。人们说“因为思维一致而更喜欢 HasValue”的建议是不相关且无用的。 你能预测一下它的性能吗?

public static bool CheckNullableGenericImpl<T>(T? t) where T: struct
{
    return t != null;
}

聚苯硫醚 人们继续减,但没有人试图预测其表现 CheckNullableGenericImpl. 。而且编译器不会帮你替换 !=nullHasValue. HasValue 如果您对性能感兴趣,应该直接使用。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top