问答中心分类: C#Nullable 和 Nullable 有什么区别<T>.HasValue 或 Nullable<T> != 空?
0
匿名用户 提问 4分钟 前

我一直用Nullable<>.HasValue因为我喜欢语义。但是,最近我正在研究其他人使用的现有代码库Nullable<> != null专门代替。
是否有理由使用其中一个,还是纯粹是偏好?

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

对比

  1. int? b;
    if (b != null)
        // ...
nailitdown 回复 4分钟 前

我问了一个类似的问题……得到了一些很好的答案:stackoverflow.com/questions/633286/…

nailitdown 回复 4分钟 前

亲自,我会用HasValue因为我认为文字往往比符号更具可读性。不过,这完全取决于您,以及适合您现有风格的方式。

nailitdown 回复 4分钟 前

.HasValue更有意义,因为它表示类型是类型T?而不是可以为空的类型,例如字符串。

4 Answers
0
cbp 回答 4分钟 前

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

Luaan 回复 4分钟 前

当然,这是非常具有误导性的,因为Nullable<>不是一个引用类型。

cbp 回复 4分钟 前

是的,但在您进行空值检查时,这一事实通常无关紧要。

Jim Balter 回复 4分钟 前

它只会误导概念上的混淆。对两种不同类型使用一致的语法并不意味着它们是相同的类型。 C# 具有可为空的引用类型(所有引用类型当前都是可为空的,但将来会改变)和可为空的值类型。对所有可空类型使用一致的语法是有意义的。这绝不意味着可空值类型是引用类型,或者可空引用类型是值类型。

Konrad 回复 4分钟 前

我更喜欢HasValue因为它比!= null

ColacX 回复 4分钟 前

如果您不混合编写相同代码的不同风格,则编码一致性更具可读性。由于并非所有地方都有 .HasValue 属性,因此使用 != null 来增加一致性。在我看来。

Captain Prinny 回复 4分钟 前

绝对投票支持这个偏好,如果没有别的,它会使编码更改更简单,因为从引用类型到可空类型不需要在其他任何地方更改代码,使用.HasValue一旦不再显式地变成不正确的语法Nullable,这可能不是一个常见的情况,但是如果你曾经为 Tuple 编写过一个结构,然后把它变成一个类,那么你就已经进入了适用的领域,并且随着 NullableRefs 的出现,这将变得很多更有可能发生。

Kissaki 回复 4分钟 前

在您访问的情况下.Value使用是有意义的.HasValue尽管。

0
Perrin Larson 回答 4分钟 前

我通过使用不同的方法将值分配给可为空的 int 对此进行了一些研究。这是我做各种事情时发生的事情。应该澄清发生了什么。记住:Nullable<something>或速记something?是一个结构,编译器似乎为此做了很多工作,让我们可以将它与 null 一起使用,就好像它是一个类一样。
正如你将在下面看到的,SomeNullable == nullSomeNullable.HasValue将始终返回预期的真或假。虽然下面没有展示,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;

无需担心 polysomthing whatchamacallits 不过,只要它工作正常? 🙂

andrewjsaid 回复 4分钟 前

“记住:可以为空或速记的东西?是一个类。”这是错误的!可以为空是一个结构。它重载 Equals 和 == 运算符以在与 null 比较时返回 true。编译器对这个比较没有花哨的工作。

Perrin Larson 回复 4分钟 前

@andrewjs – 你是对的,它是一个结构(不是一个类),但你错了它重载了 == 运算符。如果你输入Nullable<X>在 VisualStudio 2013 和 F12 中,您会看到它只会重载往返转换X, 和Equals(object other)方法。但是,我认为 == 运算符默认使用该方法,因此效果是一样的。我实际上一直想更新这个关于这个事实的答案一段时间,但我很懒和/或很忙。这个评论现在必须做:)

andrewjsaid 回复 4分钟 前

我通过 ildasm 进行了快速检查,您对编译器做了一些魔术是正确的;比较 Nullable对象为 null 实际上转换为对 HasValue 的调用。有趣的!

Luaan 回复 4分钟 前

@andrewjs 实际上,编译器做了很多工作来优化可空值。例如,如果您将值分配给可空类型,则它实际上根本不是可空的(例如,int? val = 42; val.GetType() == typeof(int))。因此,不仅可以为 null 的结构可以等于 null,而且通常根本不是可以为 null 的! 😀 同样,当你装箱一个可为空的值,你装箱int, 不是int?– 当int?没有价值,你得到null而不是装箱的可为空值。这基本上意味着正确使用可空值很少有任何开销:)

Jim Balter 回复 4分钟 前

“如果没有编译器的帮助,尝试在其变量被分配为 null 的 Nullable 上调用 HasValue 会产生空引用运行时错误。” – 这是无稽之谈。可空的是值类型,而不是引用类型,因此不可能出现空引用错误。此页面上的许多评论反映了这种混淆,将“可空”与“引用类型”混为一谈。

Jim Balter 回复 4分钟 前

@Luaan“它实际上根本不是可以为空的”——这和你的其余评论都是无稽之谈。类型可以为空;价值观不是。可空类型 T?可以是 T 或 null。当然,当它是 T 时,GetType() 返回 typeof(T),因为 GetType 产生运行类型。 “很少有开销”——当然有开销;表示该值是否为空的标志需要额外的存储空间,并且需要运行时检查来测试该标志。

Luaan 回复 4分钟 前

@JimBalter 好的,所以告诉我如何值类型可以有不同的运行从没有运行时/编译器作弊的编译时类型中键入。您可以创建自己的可空类型,其行为方式相同吗?您可以为值类型重载装箱运算符吗?你怎么超载GetType?你得到什么开销Nullable.GetValueOrDefault?在任何其他值类型的装箱之上装箱一个可为空的类型会得到什么开销?您获得开销的唯一情况是不装箱(标志)和检查 null 时(这就是您想要的!)。

Jim Balter 回复 4分钟 前

@Luaan 一个变量int?编译时类型的运行时类型为int或者Null.不是“作弊”,是执行一个抽象.一个变量Base编译时类型可以有Derived作为运行时类型——没有“作弊”。这是非常基本的。

Luaan 回复 4分钟 前

@JimBalter 真的吗?这很有趣。那么内存分析器会告诉您关于类中可为空的字段的哪些信息?如何在 C# 中声明从另一个值类型继承的值类型?如何声明自己的可空类型,其行为与 .NET 的可空类型相同?从什么时候开始Null.NET 中的一种类型?您能否指出 CLR/C# 规范中所说的部分?可空值在 CLR 规范中得到了很好的定义,它们的行为不是“抽象的实现”——它是合同.但是,如果您能做的最好的事情就是人身攻击,那就尽情享受吧。

0
Carter Medlin 回答 4分钟 前

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

Stefan Steinegger 回复 4分钟 前

我会比较喜欢HasValue因为可读性。IsNot Nothing真的是一个丑陋的表达(因为双重否定)。

jmbpiano 回复 4分钟 前

@steffan “IsNot Nothing” 不是双重否定。 “无”不是负数,它是一个离散量,即使在编程领域之外。 “这个数量不是什么。”从语法上讲,与说“这个数量不是零”完全相同。也不是双重否定。

Randy Gamage 回复 4分钟 前

不是我不想不同意这里没有真相,而是现在来吧。 IsNotNothing 显然,嗯,过于消极。为什么不写一些像 HasValue 这样积极而清晰的东西呢?这不是语法测试,而是编码,主要目标是清晰。

Kaveh Hadjari 回复 4分钟 前

jmbpiano:我同意这不是双重否定,但它是一个单一的否定,这几乎和一个简单的积极表达一样丑陋和清晰。

0
yan yankelevich 回答 4分钟 前

如果您使用 linq 并希望保持代码简短,我建议您始终使用!=null
这就是为什么:
假设我们有一些课程Foo可以为空的双精度多变的SomeDouble

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

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

在这种情况下,我建议总是选择较短的

Jeppe Stig Nielsen 回复 4分钟 前

是的,foo?.SomeDouble.HasValue在该上下文中是编译时错误(在我的术语中不是“抛出”),因为它的类型是bool?, 不只是bool. (这.Where方法想要一个Func<Foo, bool>.) 允许这样做(foo?.SomeDouble).HasValue,当然,因为它有类型bool.这就是 C# 编译器在内部将第一行“翻译”成的内容(至少形式上如此)。