Do you like the '?.' operator? Well, who doesn't? Many people like these concise null checks. However, today's article shows that the '?.' operator may be tricky sometimes. That is, it can create an illusion of safety when used in the foreach loop. Let's start with a small task. Take a look at the following code: Suppose the collection is null. Have you got any ideas on how each of the loops will run? Case #2 with ?. seems to be safer. But is that really so? The article's title should have already planted a seed of doubt in your mind. In any case, we'll try to figure this out below. We'll return to this task at the end of the article when we have more information. Note. The C# Specification uses the term "expression" to denote the following entity. In this article we use the term "enumerable expression". This may help to avoid confusion when we talk about different expressions.  First, let's recall what the ?. operator is. It won't take long. So: Now let's take a look at the foreach loop. IL code suggests that you can rewrite the above code fragment in C# without foreach. It would look something like this: Note. In some cases, foreach loop's IL code may become identical to the code for the for loop. However, the problem still persists. I think we'll have another article about the possible optimizations of the foreach loop. The collection.GetEnumerator() is the key element here. In black and white (although it depends on your color scheme), the code says that when the GetEnumerator method is called, the reference is dereferenced. If this reference is null, we get NullReferenceException. Now let's take a look at what happens in the foreach loop's enumerable expression with the ?. operator: We may rewrite this code as follows: As in the previous case, the GetEnumerator (strings.GetEnumerator) call occurs. However, note that the strings value can be null if wrapper is null. Well, that's to be expected with the ?. operator (we discussed it earlier). In this case, when trying to call the string.GetEnumerator() method, we get a NullReferenceException. That's why the ?. operator in the foreach loop's enumerable expression does not protect against null dereference. It only creates an illusion of safety. Once my colleague came to me and said — here's the code, we can't find the error. I was surprised. I recall exactly how I offered to work on the case that involved the foreach loop's enumerable expression having the null value. Checked it out. Indeed, the analyzer was not issuing warnings on the code below. The same was with this code. However, the analyzer issued a warning on the following code fragment. PVS-Studio warning: V3080 Possible null dereference. Consider inspecting 'query'. The analyzer would also issue a warning on the following code. PVS-Studio warning: V3080 Possible null dereference of method return value. Consider inspecting: GetPotentialNull(...). Why did the analyzer issue warnings for Test3 and Test4, but not for Test1 and Test2? The point is that the analyzer sees these cases as different: This differentiation helps the analyzer to handle each situation thoroughly. So, as a result, the analyzer: As a result, we improved 2 diagnostic rules: V3105 and V3153. V3105 now detects suspicious code fragments when a variable contains the result of the ?. operator. Then, the enumerable expression foreach uses this variable. PVS-Studio warning: V3105 The 'query' variable was used after it was assigned through null-conditional operator. NullReferenceException is possible. V3153 now detects cases where the foreach loop's enumerable expression directly uses the ?. operator. PVS-Studio warning: V3153 Enumerating the result of null-conditional access operator can lead to NullReferenceException. Consider inspecting: collection?.Where(predicate). It's a great feeling to see the analyzer works better! As I've already said, we regularly test the analyzer on open-source projects. So, after we improved V3105 and V3153, we managed to find some new triggerings! Note. This code was up to date when we added the projects to our tests. By now the code could have changed and may not contain these code fragments. I intentionally listed the entire code fragment. You will probably agree this issue is not very obvious. And of course, it's easier to find something if you know what you're looking for. ;) If you simplify the code, the issue becomes more obvious. The newInternalDestinations variable takes the ?. operator's result. If newRecord.Topology is null, newInternalDestinations will also be null. When the execution flow reaches the foreach loop, the NullReferenceException exception will be thrown. PVS-Studio warning: V3105 The 'newInternalDestinations' variable was used after it was assigned through null-conditional operator. NullReferenceException is possible. ReplicationLoader.cs 828 What's more interesting, the DatabaseTopology.FindChanges method takes the newInternalDestinations variable as the newDestinations parameter and checks it for null. PVS-Studio warning: V3153 Enumerating the result of null-conditional access operator can lead to NullReferenceException. Consider inspecting: properties?.Keys. MockEngine.cs 159 Here the foreach directly contains the ?. operator. Perhaps the developer thought the ?. operator would protect from NullReferenceException. But we know that it's not safer. ;) This example is similar to the previous one.  PVS-Studio warning: V3153 Enumerating the result of null-conditional access operator can lead to NullReferenceException. NLogLogger.cs 50 Also, the developers used the ?. operator directly in the foreach loop's enumerable expression to avoid NullReferenceException. Maybe they'll get lucky, and the Configuration property will never return null. Otherwise, some time later this code may play a trick on you. PVS-Studio warning: V3153 Enumerating the result of null-conditional access operator can lead to NullReferenceException. CompletionSource.cs 482 That's great, isn't it? I love it when PVS-Studio finds interesting cases in compilers or other analyzers. And now it's time to admit that we are not perfect either. We've made the same mistakes. :)   We regularly check PVS-Studio with PVS-Studio. This is how it works: And so, after we've improved V3153 and V3105, the analyzer issued several warnings on our code. Indeed, the analyzer detected cases when the foreach loop's enumerable expression contained the ?. operator. Also, we found indirect cases (when a variable takes a value). We were lucky that we hadn't been getting an exception. In any case, we've already taken the warnings into account and fixed the corresponding cases. ;) Here's a code fragment that triggered a warning: Yeah, there's a bunch of ?. here. Try to find the one that will shoot you in the foot. It seems like ?. operators provide maximum safety (use the Crysis nanosuit voice effect while reading) for your code, but in fact, that's not true. Of course, you can do that. And we've seen such code examples. For example, the ?? operator can come to the rescue. The following code is dangerous and may lead to NullReferenceException: And this code will not throw an exception: While the ?. operator returns a null value, the ?? operator results in Enumerable.Empty<String>(). Therefore, there will be no exception. However, adding an explicit null check instead could be a good idea. Obviously, it looks not so modern but clear and easy to read. As you may remember, we started the article with the following task: Now you know that option #2 is not safe at all. It won't help you avoid NullReferenceException. And what about option #1? At first glance, it seems that we'll have NullReferenceException when calling collection.NotNullItems(). But that's not necessarily true! Suppose NotNullItems is an extension method with the following body: As we can see, the method checks collection for null. Since in this case the method returns the Enumerable.Empty<T>() value, there will be no exception. That is, loop #1 works successfully, even if collection is null. But the second loop remains dangerous. If collection is null, the NotNullItems method is not called. Therefore, check for null does not work. As a result, we have the same situation which we kept seeing time and again — an attempt to call the GetEnumerator() method for a null reference. That's one interesting case we have! Calling the collection.NotNullItems() method explicitly prevents NullReferenceException, but a "safe" call — collection?.NotNullItems() — does not. We have several conclusions here: We, as developers once again realized that it's important not only to develop new diagnostics but also to refine the existing ones. PVS-Studio 7.13 includes the updates we discussed in this article. Do you want to know whether anyone uses the ?. operator in the enumerable expression in your code base? Then, feel free to download the analyzer from the website and check the code. As usual, feel free to follow me on Twitter. Независимый эксперт