在c#中访问可空值的最佳方法

时间:2012-05-02 14:23:56

标签: c# nhibernate nullable hibernate-criteria

在我们的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

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

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

4 个答案:

答案 0 :(得分:24)

更新:这个问题是the subject of my blog on December 20th, 2012。我将在2013年12月和1月初的更多关于此优化的想法中跟进。感谢您提出的好问题!


  

如果我们强制使用另一种方法来查找值而不是明确调用.Value

,我只是想看看性能是否受到打击。

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

其次,你在一般意义上提出错误的问题。问题不应该是“是否会对性能产生影响?”而是“是否存在不可接受的对性能的影响?”而且,我们不知道您接受或不接受的是什么。

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

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

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

return this.value; 

那有什么区别? 抖动可能不会内联前一种方法,因此您需要支付高达几纳秒的惩罚以使额外的呼叫间接。

如果几纳秒的罚款与您相关,那么(1)祝贺您编写的程序仅在几微秒内运行,并且(2)您将需要非常谨慎地测量 看看是否有任何真正的区别,因为纳秒很小。

答案 1 :(得分:13)

  

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

int yourInt = myInt.Value;
如果HasValuefalse,则会抛出

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

请注意

int yourInt = myInt;

不合法(编译时失败),因为没有从int?int的隐式转换(如果值为null会怎么样;没有合理的价值来转换{ {1}}至)。你可以说:

int?

请注意,如果int yourInt = (int)myInt; myInt,则会抛出此内容,就像访问null一样。

最后一点,如果您接受默认值,则可以使用null coalescing operator并说:

Value

这相当于:

int yourInt = myInt ?? defaultValue;
  

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

没有。这里的性能完全不相关,特别是如果涉及数据库的话。这绝对没有任何有意义的性能差异,并且肯定不会成为瓶颈。只需编写最清晰的代码即可。坦率地说,我发现int yourInt; if(myInt.HasValue) { yourInt = myInt.Value; } else { yourInt = defaultValue; } 是最清楚的。

答案 2 :(得分:2)

此代码无效:

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

  // do something with yourInt
}

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

int yourInt = (int) myInt;

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

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

答案 3 :(得分:-2)

您有理由无法直接致电.Value:当yourInt为空时,您期望myInt的价值是什么?

如果你想让它成为一个默认值(例如:0),你可以在可空的int上创建一个扩展方法,如.ValueOrDefault()