在我们的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
,我只是想看看性能是否受到打击。
答案 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;
如果HasValue
为false
,则会抛出。
因此,如果您不想体验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()
。