9

在我们的 C# 代码中,我们已经在尝试访问变量之前测试了它是否为空。

if (myInt.HasValue) {
  var yourInt = myInt;

  // do something with yourInt
}

我的问题:使用可空属性是否有区别,就好像它不是在经过测试之后与以下一样?

if (myInt.HasValue) {
  var yourInt = myInt.Value; // see the difference?

  // do something with yourInt
}

这只是一个偏好问题,还是.Value即使在可空对象通过了该测试之后使用也有明显的原因或性能影响?

更新

我扩展了我的第二个示例,我们已经使用 进行了测试HasValue,但是我们使用它.Value来访问该值。

更新 2

我更新了使用vars 的示例,因为在我们的代码中我们实际上并没有使用int类型,很抱歉这个糟糕的示例。在我们的代码中,我们实际上只是使用 NHibernate Criteria 查询中的对象 -Expression.Eq("thing", myInt)查询。

这不会引发编译错误。我试图在不涉及 NHibernate 的情况下简化示例以找到问题的根源。抱歉,如果这会使某些答案无效。我只是想看看如果我们强制使用另一种方法来查找值而不是显式调用,是否会影响性能.Value

4

4 回答 4

24

更新:这个问题是我在 2012 年 12 月 20 日的博客的主题。我将在 2013 年 12 月下旬和 2013 年 1 月上旬对此优化提出更多想法。感谢您提出的好问题!


我只是想看看如果我们强制另一种方法来查找值而不是显式调用 .Value 是否会影响性能

首先,如果你有一个性能问题,那么你是唯一可以回答这个问题的人,你可以通过秒表两种方式来回答,看看哪个更快。为什么人们经常在 StackOverflow 上问这个问题对我来说是个谜。这就像在说“嘿,互联网,这是两匹马的照片。哪一匹跑得更快?” 我们应该怎么知道? 与他们比赛,然后你就会知道。他们是你的马。

其次,您在一般意义上提出了错误的问题。问题不应该是“对性能有影响吗?”,而应该是“对性能有不可接受的影响吗?” 再说一次,我们不知道您可以接受或不接受什么。

第三,您在非常具体的意义上提出了错误的问题。在这里要问的正确问题是,如果您对纳米级优化感兴趣,使用 Value getter 或 GetValueOrDefault 方法哪个更快?

答案是通常 GetValueOrDefault 方法比 Value 更快。为什么?因为区别在于:

if (this.HasValue) return this.value; else throw new Exception();

return this.value; 

那里有什么区别? 抖动可能不会内联前一种方法,因此您要付出高达几纳秒的代价来进行额外的调用间接。

如果几纳秒的惩罚与您相关,那么(1)恭喜您编写了一个仅在几微秒内运行的程序,以及(2)您将需要非常仔细地测量以查看是否有任何真正的差异,因为纳秒非常小。

于 2012-05-02T15:31:18.663 回答
15

这只是偏好问题还是有明显的原因

int yourInt = myInt.Value;

HasValue如果is会抛出false

因此,如果您不想体验InvalidOperationExceptions,请先检查HasValue

注意

int yourInt = myInt;

是不合法的(编译时失败),因为没有从int?to的隐式转换int(如果值是null; 没有合理的值来转换int?to)。你可以说:

int yourInt = (int)myInt;

请注意,这将抛出 if myIntis null,就像访问Value.

最后一点,如果您可以接受默认值,您可以使用null 合并运算符并说:

int yourInt = myInt ?? defaultValue;

这相当于:

int yourInt;
if(myInt.HasValue) {
    yourInt = myInt.Value;
}
else {
    yourInt = defaultValue;
}

这不会引发编译错误。我试图在不涉及 NHibernate 的情况下简化示例以找到问题的根源。抱歉,如果这会使某些答案无效。我只是想看看如果我们强制使用另一种方法来查找值而不是显式调用.Value,是否会对性能产生影响。

不。性能在这里完全无关紧要,尤其是在涉及数据库的情况下。这绝对不会有任何有意义的性能差异,也肯定不会成为瓶颈。只写最清晰的代码。坦率地说,我觉得.Value是最清楚的。

于 2012-05-02T14:24:37.337 回答
2

此代码无效:

if (myInt.HasValue) {
  int yourInt = myInt;

  // do something with yourInt
}

如果您不想使用该Value属性,则必须改用显式强制转换,或空合并运算符,或GetValueOrDefault,例如

int yourInt = (int) myInt;

无论如何,演员表最终被编译为对该Value属性的访问,IIRC。

所以是的,即使你测试了一个值是非空的,你仍然需要做一些事情来将它转换为基础值——只是为了让它编译。编译器不会尝试分析“可能的无效性”(例如,想象一下它是否是一个实例变量,它可能由于访问之间的线程而改变)。它只是遵循允许您对可空表达式执行的操作的规则,并且隐式转换为不可空类型不是您被允许执行的操作之一......

于 2012-05-02T14:26:46.967 回答
-2

您不能直接调用是有原因的:当为空时,.Value您期望 的值是多少?yourIntmyInt

当您希望它具有默认值(例如:0)时,您可以在可为空的 int 上创建一个扩展方法,例如.ValueOrDefault().

于 2012-05-02T14:29:02.513 回答