C# nullable check verify that property is not null - c#

I have a complex object I want to check is valid. I have a method to check it, but C# nullability checks don't know about them:
#nullable enable
...
bool IsValid(Thing? thing)
{
return thing?.foo != null && thing?.bar != null;
}
...
if(IsValid(thing))
{
thing.foo.subProperty; // CS8602 warning: foo is possibly null here
Is there any way to apply something like TypeScript's type-gates here? If IsValid is true then we know that thing.foo is not null, but the C# analyser can't see inside the function. If I inline the check it can, but then I'm copy-pasting code and this is a simple example.
Is there any annotation or pattern where I can have the nullability analysis with the more complex checks?

You can use the null-forgiving operator like this:
if (IsValid(thing))
{
thing.foo!.subProperty;
}
As #JeroenMostert points out, while it won't help you with foo, an alternative is the NotNullWhen attribute.
The compiler knows thing is not null here, but foo might still be null.
bool IsValid([NotNullWhen(true)] Thing? thing)
{
return thing?.foo != null && thing?.bar != null;
}
if (IsValid(thing))
{
thing.foo;
}

Related

Why does C# compiler thinks this nullabale reference can be null?

I have this code where the compiler says that myObj in ValidateMyObj(myObj) may be null.
Since it has to be not-null for string.IsNullOrEmpty to return false and enter the condition, how can it consider it could be null?
But most of all, how can it then consider that it cannot be null in myObj.Exists?
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (!string.IsNullOrEmpty(myObj?.StrProp))
{
ValidateMyObj(myObj);
if (ViewBag.IsValid)
myObj.Exists = true;
}
Edit: Playing with explicit null-check
Simply adding the explicit null-check gives the same behaviour:
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (myObj != null && !string.IsNullOrEmpty(myObj?.StrProp))
{
ValidateMyObj(myObj);
if (ViewBag.IsValid)
myObj.Exists = true;
}
this compiles to (according to the ILSpy decompiler when deactivating the decompilation of the ? operator)
MyObjType myObj = myObjCol.FirstOrDefault(x => x.Member);
if (myObj != null && !string.IsNullOrEmpty((myObj != null) ? myObj.StrProp : null))
{
ValidateMyObj(myObj);
if (ViewBag.IsValid)
myObj.Exists = true;
}
On the other hand, if I then remove the null-conditional operator, then the squiggly line disapears.
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (myObj != null && !string.IsNullOrEmpty(myObj.StrProp))
{
ValidateMyObj(myObj);
if (ViewBag.IsValid)
myObj.Exists = true;
}
In fact, it also disapears if I remove the explicit check and the null-conditional operator nor does it give one on the argument passed to ÌsNullOrEmpty`:
var myObj = myObjCol.FirstOrDefault(x => x.Member);
if (!string.IsNullOrEmpty(myObj.StrProp))
{
ValidateMyObj(myObj);
if (ViewBag.IsValid)
myObj.Exists = true;
}
If I got this right then i think it is because the IsNullOrEmpty is refering to the StrProp but the warning targets the myObj
Clarification:
if myObj is null then there isn't a StrProp to check for
The null-state analysis of the C# compiler tries to keep track of the null-state of every variable based on the context where it is used.
The null-state might be:
The value is known to be null.
The value is known to be not null.
The null-state is unknown (maybe-null).
After the assignment var myObj = myObjCol.FirstOrDefault(x => x.Member); myObj is in the maybe-null state (this actually depends on the framework – see below).
The call to string.IsNullOrEmpty doesn't change the null-state. The string.IsNullOrEmpty method is just an ordinary method. Without extra knowledge the compiler doesn't know anything about the null-state of the parameter.
It turns out that in .NET 6 the compiler gets this extra help. In .NET 6 the signature of that method is:
public static bool IsNullOrEmpty([NotNullWhen(false)] string? value);
In .NET Framework however the signature is:
public static bool IsNullOrEmpty(string value);
The NotNullWhen attribute tells the compiler that the argument passed to string.IsNullOrEmpty is not null when the method returns false. Therefore in .NET 6 you don't get any warnings in your original code because the compiler changes the null-state to not-null inside the if-block. In .NET Framework 4.8 (or earlier) it can't do that so the null-state inside the if-block is the same as outside.
Surprisingly after adding the explicit null check for myObj the squiggly line doesn't go away.
if (myObj != null && !string.IsNullOrEmpty(myObj?.StrProp)) { ... }
is equivalent to
if (myObj != null)
{
// null-state of myObj is not-null
if (!string.IsNullOrEmpty(myObj != null ? myObj.StrProp : null))
{
// the second test changed the null-state of myObj to maybe-null
}
}
When you now remove that second test the null-state doesn't change and the warning goes away.
The framework also influences how the return value of the FirstOrDefault method is treated.
Right at the beginning you have:
var myObj = myObjCol.FirstOrDefault(x => x.Member);`
Obviously this should set myObj to the maybe-null state and should give a warning when you then dereference the variable without checking for null:
if (!string.IsNullOrEmpty(myObj.StrProp)) { ... } // should warn: Dereference of a pssibly null reference
It seems that it doesn't do that when you are on .NET Framework. But it does when you are on .NET 6.
This can be explained when you look at the history of C# and .NET.
C# 8 was the first version to support nullable reference types. It was released shortly after the last version of .NET Framework (4.8).
In .NET Framework the signature is:
public static TSource FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
In .NET 6 it is:
public static TSource? FirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
This means that the nullability context is unclear when you call FirstOrDefault<T> on .NET Framework but it is clear on .NET 6.
As far as I am aware the C# compiler team has decided not to warn when the context is unclear because that would give too many false warnings and would annoy too many people.
Having said that: If you want more reliable nullability analysis you should use a version of the framework which is fully null-aware.

Do null objects always have to be specified in latest c# code?

Please consider the following code:
public IList<string> DoSomething(SomeModel model)
{
if (model == null)
{
return new List<string> {"model is null"};
}
...
}
When I wrote the above code I keep getting a suggestion from Resharper that says "Expression is always false"
But I don't understand why because the expression here can most definitely be true if I pass a null object.
Now consider me calling this method:
DoSomething(null);
Here I get a warning from resharper that says Cannot convert null literal to non-nullable reference type.
Is there some new change in c# code that I'm just simply not aware of? We have to explicitly now specify null objects and if so what is the new "null" value?

Code Optimisation - remove this expression which always evaluates to "true"

I am trying to optimize the code using SonarQube
List<string> itemList = serviceObject.GetItems();
I tried to validate the list with the below code
if(itemList != null && itemList.Any()
{
//Some operations
}
when above code executed I am getting Sonarqube error remove this expression which always evaluates to "true"
So I refactor the code as
if(itemList == null || !itemList.Any())
return;
//Some Operations
when above code executed I am getting Sonarqube error remove this expression which always evaluates to "false"
Could anyone let me know what is wrong here?
You can shorten this to
if (itemList?.Count >0)
{
...
}
or
if (itemList?.Any() ==true)
{
...
}
?. is one of the Null conditional operators (the other is ?[]), the Elvis operator for short, that allows you to access potentially null variables without throwing. The result of the entire expression after the Elvis operator is nullable and returns null if the variable is null.
This means that itemList?.Count returns an Nullable<int>, and itemList?.Any() a Nullable<bool>. Nullable<T> defines relational operators between itself and its base type T but can't be used as T without an explicit cast. That's why (itemList?.Any() ==true) is needed.
If you use Nullable Reference Types though, itemList can't be null, so a simple comparison would be enough:
if (itemList.Count >0)
{
...
}
If you enable Nullable Reference Types by setting #nullable enable in a source file or <Nullable>enable</Nullable> in the csproj file, the compiler ensures that all variables, fields and methods that return reference types are not null, forcing you to either fix the problem or explicitly specify that a variable is nullable.
With NRTs enabled this line :
List<string> itemList = serviceObject.GetItems();
Would only compile without warnings if GetItems never returned a null. If the compiler doubts this, it would issue a warning advising you to either fix the problem or explicitly declare itemList as nullable with :
List<string>? itemList = serviceObject.GetItems();
I believe it is due to null Comparision.
itemList != null
There is a high chance that serviceObject.GetItems(); is guaranteed to not return null by having a [NotNull] Annotation. Hence the null check is redundant.
How can I show that a method will never return null (Design by contract) in C#
if( itemList !=null ) {//Do something...}
List<string> itemList = new List<string>();
itemList = serviceObject.GetItems();
if(itemList!=null && itemList.Count() > 0 )
{
//Some operations
}
Count is enough to validate a list
List will always not be null but will have no elements. but you have to instatiate it so it wont throw an exception.

Resharper: Possible null assignment to entity marked with "NotNull" attribute

I apologize if this question is a bit obsessive but I do like my code not to have any wiggly lines underneath it where resharper is telling me off.
I have a generic list:
var permissions = new List<Permission>();
at some point in the code I need to test the first element:
if (permissions.First().ImportID == this.ImportId)
{
// do stuff
}
Resharper (rightly) complains that permissions could be null, so I take its advice and add a check in:
if (permissions != null && permissions.First().ImportID == this.ImportId)
{
// do stuff
}
Now I realise that I need to protect against the list being empty so I add a check for any in there too:
if (permissions != null && permissions.Any() && permissions.First().ImportID == this.ImportId)
{
// do stuff
}
and life is good, the code works & resharper is quiet. Realising that the null + any() check is going to be used quite often I add an extension method:
public static bool IsEmpty<T>(this IEnumerable<T> source)
{
if (source == null)
return true;
return !source.Any();
}
The only problem is now when I use this:
if (!permissions.IsEmpty() && permissions.First().ImportID == this.ImportId)
Resharper starts moaning again "Possible null assignment to entity marked with "NotNull" attribute".
So, is there a way to let resharper know that permissions will never be null after IsEmpty() has executed (in the same way it understands what != null does) or is my only option to ignore the message.
I suspect you can use a R# annotation for this. Something like:
[ContractAnnotation("null => true")]
public static bool IsEmpty<T>(this IEnumerable<T> source)
{
return source == null || !source.Any();
}
I believe that should give R# enough information to work out that you're not trying to use a null reference if IsEmpty returns false.

Is it possible for null-conditionals to not return nullable value types while checking conditions?

When assigning a value from a null-conditional, it makes sense to return a nullable value type. Otherwise, if the object is null, the null-conditional would return the value type's default and you don't want that. Therefore, this is good:
bool? IsTerminated = Employee?.IsTerminated;
However, why does it return a nullable type if I am just checking a condition? You would think that the compiler could figure this out just fine:
if (Employee?.IsTerminated) { /*do something here*/ }
After all, it's just compiling down to this, right?
if (Employee != null && Employee.IsTerminated) { /*do something here*/ }
In order to get it to work, I have to do this:
if ((Employee?.IsTerminated).GetValueOrDefault()) { /*do something here*/ }
Between the extra code and having to wrap the expression in parens, the whole purpose of the null-conditional's short-hand syntax appears to be defeated. Is this the proper way to handle a null-conditional return value or is there another way that doesn't involve accounting for a nullable return value?
If A?.B contains B of a reference type, then you'll get a return of type B. Otherwise, if B is a value type, the return type is a nullable wrapping around type B. So, that's a short answer to your question.
With that being said, in your case, since you're getting back a bool, it's understandable that it'll be wrapped as bool?. Knowing that, you should simply work with it as you would normally would with bool?. One way is to simply do an equality comparison to a desired value of bool.
For example:
if (Employee?.IsTerminated == true) { }
It's a bit shorter and easier to read than:
if ((Employee?.IsTerminated).GetValueOrDefault()) { }
The example comparison works, because null will never equal to a bool.
You can always use the null coalesce operator:
if (Employee?.IsTerminated ?? false) { /*do something here*/ }

Categories