Click here to Skip to main content
15,868,141 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I thought using 'pragma for suppression was weird enough ... until I ran across ...

I have a function that returns a Tuple in the form of:

(IEnumerable<instance>, IEnumerable<instance>):

there are two cases where a null result may terminate the function, and in those cases I want to return an equivalent to a null Tuple. I use this type of termination statement:

if (firstresults == null) return (null, null)!;

Notice that exclamation point at the end ? ReSharper suggested that; I removed it and the code still compiles; so I got curious what the '!' did: turns out it's a C#8 feature called "null forgiving:" [^]
Quote:
Available in C# 8.0 and later, the unary postfix ! operator is the null-forgiving, or null-suppression, operator. In an enabled nullable annotation context, you use the null-forgiving operator to declare that expression x of a reference type isn't null: x!. The unary prefix ! operator is the logical negation operator.

The null-forgiving operator has no effect at run time. It only affects the compiler's static flow analysis by changing the null state of the expression. At run time, expression x! evaluates to the result of the underlying expression x.


What I have tried:

scratching my head, searching for an explanation until one found me
Posted
Updated 13-Jan-22 5:53am
v6
Comments
lmoelleb 13-Jan-22 9:47am    
No idea why ReSharper suggested that (stopped using it years ago) in this case. Null-suppression is a useful feature for the few cases where you know something is not going to be null, but the compiler can't figure it out. If only makes sense if you enable "nullable" support which luckily is now the default for new projects.
Richard Deeming 13-Jan-22 11:46am    
NB: You could replace return (null, null); with return default; and get the same return value. :)
George Swan 13-Jan-22 17:53pm    
It seems to me that this is an example of over-engineering the C# language. I am expecting the Not- Elvis operator !?. to be implemented before long.
BillWoodruff 14-Jan-22 3:51am    
As over-engineered as the Pragma weirdness :) ?

1 solution

This is part of the (relatively-new) "nullable reference types" feature:
Nullable reference types | Microsoft Docs[^]

When enabled, the compiler assumes that any reference type must not contain null unless it is marked as "nullable" by appending ? to the type name.

Technically, therefore, your method should be declared as returning:
C#
(IEnumable<instance>?, IEnumable<instance>?)

Unless of course you could return a non-null enumerable with a null item, in which case it should be:
C#
(IEnumerable<instance?>?, IEnumerable<instance?>?)

The "null forgiving operator" is intended for cases where the developer thinks they know better than the compiler - the static flow analysis cannot determine that the value is not null, but the developer is absolutely certain that it cannot be null. For example: Lying to the compiler | Jon Skeet's coding blog[^]

Unfortunately, that means it can be abused, as in this case: you've told the compiler that null is not null. :)
 
Share this answer
 
Comments
BillWoodruff 13-Jan-22 12:03pm    
+5 as usual :)

i am wondering if there's a better syntax for returning a null value for a Tuple like this where type T in an IEnumerable<t> is a reference type.

(null, null) does work.
BillWoodruff 14-Jan-22 3:50am    
One other thought: is there something special about the Tuple Type that is a factor here ? In .NET 4.0, the new Tuple Type is a reference type; then, in 7.0, the "literal" Tuple (ValueType) was introduced.

So, if I'm using a Tuple that I believe is not literal: why, if I just return null, does the compiler shout:

Error CS0037 Cannot convert null to '(IEnumerable<flight>, IEnumerable<flight>)' because it is a non-nullable value type

Clearly it sees the Tuple I define as a ValueType which explains why returning 'default works, but, makes the fact (null,null) works ... strange.
Richard Deeming 14-Jan-22 4:26am    
It already is a value tuple - the (value1, value2, ...) syntax defines a ValueTuple, not a Tuple.
Tuple types - C# reference | Microsoft Docs[^]

If the return type was Tuple<IEnumerable<instance>?, IEnumerable<instance>?>, then you'd have to return new(null, null); instead of return (null, null); (which wouldn't compile) or return default; (which would return a null tuple instance, and generate a nullable warning).
BillWoodruff 14-Jan-22 4:32am    
Thanks ! I expanded the comment you just responded to. The second type declaration in your first example does not have a ? suffix ... accident ?

Since an IEnumerable is by definition nullable, why is the ? required ?
Richard Deeming 14-Jan-22 4:38am    
Yes, I accidentally missed the ? from the second type parameter. :)

Will nullable reference types, <code>IEnumerable<T> is not nullable; the compiler will complain if you try to assign null to it. To make it nullable, it needs to be IEnumerable<T>?.

Personally, I'm starting to think the whole "nullable reference types" system is a mess. This sort of thing needs to be baked into the language, framework, and compilers from the start. As it stands, any "not nullable" reference types still need to be checked for null, since the code could be called from code compiled without NRT warnings enabled; or code built by a compiler which doesn't understand NRTs; or called via reflection, so the compiler has no way of generating a warning.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900