简体   繁体   English

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

[英]Best way to access nullable values in c#

In our C# code, we already test if a variable is null before attempting to access it. 在我们的C#代码中,我们已经在尝试访问变量之前测试了变量是否为null。

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

  // do something with yourInt
}

My question: is there a difference in using the nullable property as if it wasn't after it has been tested versus the following? 我的问题:使用可空属性是否有区别,就好像它在测试之后不是以下情况一样?

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

  // do something with yourInt
}

Is this just a matter of preference or is there a distinct reason or performance impact for using .Value even after the nullable object has passed that test? 这只是一个偏好问题,或者即使在可以为空的对象通过该测试之后,使用.Value还是有明显的原因或性能影响吗?

UPDATE UPDATE

I expanded on my second example, we already test with HasValue , but there we use .Value to access the value. 我扩展了我的第二个例子,我们已经使用HasValue测试,但我们使用.Value来访问该值。

UPDATE 2 更新2

I updated the examples to use var s because in our code we don't actually use int types, sorry about the poor example. 我更新了示例以使用var s,因为在我们的代码中我们实际上并没有使用int类型,抱歉这个糟糕的例子。 In our code, we actually just use the object inside an NHibernate Criteria query - Expression.Eq("thing", myInt) query. 在我们的代码中,我们实际上只是使用NHibernate Criteria查询中的对象 - Expression.Eq("thing", myInt)查询。

This doesn't throw a compilation error. 这不会引发编译错误。 I was trying to simplify the example to get to the root of my question without getting NHibernate involved. 我试图简化示例以获得我的问题的根源而不涉及NHibernate。 Sorry if this invalidates some of the answers. 很抱歉,如果这会使某些答案无效。 I was just trying to see if there is a hit on performance if we force another method to find the value versus explicitly calling .Value . 如果我们强制使用另一种方法来查找值而不是显式调用.Value我只是想看看性能是否受到打击。

UPDATE: This question was the subject of my blog on December 20th, 2012 . 更新:这个问题是我2012年12月20日的博客主题 I'll be following it up with more thoughts on this optimization later in December and early January 2013. Thanks for the great question! 我将在2013年12月和1月初的更多关于此优化的想法中跟进。感谢您提出的好问题!


I was just trying to see if there is a hit on performance if we force another method to find the value versus explicitly calling .Value 如果我们强制使用另一种方法来查找值而不是显式调用.Value,我只是想看看性能是否受到打击

First off, if you have a performance question then you are the only person who can answer the question , and you answer it by trying it both ways with a stopwatch and seeing which one is faster . 首先,如果您有性能问题,那么您是唯一可以回答问题的人 ,并且您可以通过秒表尝试两种方式并查看哪一个更快 来回答问题 It is a mystery to me why people ask this question on StackOverflow so often. 对我来说,为什么人们经常在StackOverflow上问这个问题,这是一个谜。 It is exactly like saying "Hey internet, here are pictures of two horses. Which one runs faster?" 这就像说“嘿,互联网,这里有两匹马的照片。哪一个跑得更快?” How should we know? 我们怎么知道? Race them, and then you'll know. 比赛他们,然后你会知道。 They're your horses. 他们是你的马。

Second, you are asking the wrong question in a general sense. 其次,你在一般意义上提出错误的问题。 The question should not be "is there a hit on performance?", but rather "is there an unacceptable hit on performance?" 问题不应该是“是否会对性能产生影响?”而是“对性能造成不可接受的打击?” And again, we do not know what is or is not acceptable to you. 而且,我们不知道您接受或不接受的是什么。

Third, you are asking the wrong question in a very specific sense. 第三,你是在非常具体的意义上提出错误的问题。 The correct question to ask here, if you are interested in nano-scale optimization, is which is faster, using the Value getter or the GetValueOrDefault method? 这里要问的正确问题是,如果您对纳米级优化感兴趣,使用Value getter或GetValueOrDefault方法哪个更快?

The answer is that typically the GetValueOrDefault method is faster than Value. 答案是通常GetValueOrDefault方法比Value快。 Why? 为什么? Because the difference is the difference between: 因为区别在于:

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

and

return this.value; 

What's the difference there? 那有什么区别? The jitter will likely not inline the former method and therefore you are paying a penalty of up to several nanoseconds to make the extra call indirection. 抖动可能不会内联前一种方法 ,因此您需要支付高达几纳秒的罚款才能实现额外的呼叫间接。

If a penalty of several nanoseconds is relevant to you then (1) congratulations for writing a program that runs in only a few microseconds, and (2) you are going to need to measure extraordinarily carefully to see if there is any real difference, because nanoseconds are pretty darn small. 如果几纳秒的罚款与你相关,那么(1)祝贺编写一个只运行几微秒的程序,(2)你需要非常仔细地测量,看看是否有任何真正的差异,因为纳秒非常小。

is this just a matter of preference or is there a distinct reason 这只是一个偏好问题还是有一个明显的原因

int yourInt = myInt.Value;

will throw if HasValue is false . 如果HasValuefalse则抛出

Thus, if you don't want to experience InvalidOperationException s, check HasValue first. 因此,如果您不想体验InvalidOperationException ,请首先检查HasValue

Note that 注意

int yourInt = myInt;

is not legal (compile-time failure) because there is no implicit conversion from int? 是不合法的(编译时失败)因为int?没有隐式转换int? to int (what if the value is null ; there's no reasonable value to cast the int? to). to int (如果值为null ,那么将int?为没有合理的值)。 You can say: 你可以说:

int yourInt = (int)myInt;

Note that this will throw if myInt is null though, just like accessing Value . 请注意,如果myIntnull ,则会抛出此null ,就像访问Value

One last note, if you're okay with accepting a default value, you can use the null coalescing operator and say: 最后一点,如果您接受默认值,则可以使用null合并运算符并说:

int yourInt = myInt ?? defaultValue;

This is equivalent to: 这相当于:

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

This doesn't throw a compilation error. 这不会引发编译错误。 I was trying to simplify the example to get to the root of my question without getting NHibernate involved. 我试图简化示例以获得我的问题的根源而不涉及NHibernate。 Sorry if this invalidates some of the answers. 很抱歉,如果这会使某些答案无效。 I was just trying to see if there is a hit on performance if we force another method to find the value versus explicitly calling .Value. 如果我们强制使用另一种方法来查找值而不是显式调用.Value,我只是想看看性能是否受到打击。

No. Performance is totally irrelevant here, especially if there is a database involved. 没有。性能在这里完全无关紧要,特别是如果涉及数据库的话。 This is absolutely not going have any meaningful performance difference, and will certainly not be a bottleneck. 这绝对没有任何有意义的性能差异,并且肯定不会成为瓶颈。 Just write the clearest code. 只需编写最清晰的代码即可。 Frankly, I find .Value to be the clearest. 坦率地说,我发现.Value是最清楚的。

This code is invalid: 此代码无效:

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

  // do something with yourInt
}

If you didn't want to use the Value property, you'd have to use an explicit cast instead, or the null coalescing operator, or GetValueOrDefault , eg 如果您不想使用Value属性,则必须使用显式强制转换,或使用null合并运算符或GetValueOrDefault ,例如

int yourInt = (int) myInt;

The cast ends up being compiled as access to the Value property anyway, IIRC. 无论如何,演员最终被编译为对Value属性的访问,IIRC。

So yes, even after you've tested that a value is non-null, you still have to do something to convert it to the underlying value - just to get it to compile. 所以,是的,即使你已经测试过一个值是非空的,你仍然需要做一些事情来将它转换为底层值 - 只是为了让它进行编译。 The compiler doesn't try to analyze the "possible nullity" (imagine if it's an instance variable, for example, which could change due to threading between accesses). 编译器不会尝试分析“可能的无效”(例如,如果它是一个实例变量,例如,由于访问之间的线程可能会改变)。 It just follows the rules for what you're allowed to do with a nullable expression, and implicit conversion to the non-nullable type isn't one of those things you're allowed to do... 它只遵循允许对可以为空的表达式执行的操作的规则,并且隐式转换为非可空类型不是您允许执行的操作之一...

There is a reason why you can't call .Value direct: what would you expect the value of yourInt to be when the myInt is null? 有一个原因你不能直接调用.Value :当myInt为null时,你会期望yourInt的值是多少?

When you want to have it a default value (for instance: 0), you can create an extensionmethod on the nullable int like .ValueOrDefault() . 如果要使其具有默认值(例如:0),则可以在可空的int上创建扩展方法,如.ValueOrDefault()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM