How to suppress Possible Null Reference warnings - c#

I am playing with the nullable types in c# 8 and I found a problem that is bugging me.
Suppose I have a method which takes a nullable parameter. When a parameter is null, I want to throw a specific Exception. But I want the method to be clean and check the parameter somewhere else. The check method throws an exception, so after the method the parameter can not be null.
Unfortunately, the compiler does not see that and throws warnings at me.
Here's the method:
public void Foo(string? argument)
{
GuardAgainst.Null(argument, nameof(argument));
string variable = argument; // <-- Warning CS8600 Converting null literal or possible null value to non - nullable type
var length = argument.Length; //<--Warning CS8602 Dereference of a possibly null reference
}
Here's the check method:
public static void Null(string? text, string paramName)
{
if (text == null)
throw new ArgumentNullException(paramName);
}
Now, I can suppress the warning like this:
#pragma warning disable CS8602
var length = argument.Length;
#pragma warning restore CS8602
but it kind of kills my intention to keep my code clean.
So my question is: is there a nicer way to suppress the warnings? Or maybe tell a compiler that from now on the parameter is guaranteed to not be null?

This does what you want:
public static void Null<T>([NotNull] T? value, string paramName)
{
if (value == null)
throw new ArgumentNullException(paramName);
}
The [NotNull] attribute instructs the analysis that, after calling this method, value will not be null.
This means you don't need the ! operator, which is much cleaner and more natural.
void M(string? argument)
{
GuardAgainst.Null(argument, nameof(argument));
string variable = argument; // no warning
// ...
}
The use of an unconstrained generic type parameter T here means that this approach works for both reference types (such as string) and nullable value types (such as int?).
If you're using .NET 6, you can simplify this even further via CallerArgumentExpressionAttribute as follows:
public static void Null<T>(
[NotNull] T? value,
[CallerArgumentExpression(parameterName: "value")] string? paramName = null)
{
if (value == null)
throw new ArgumentNullException(paramName);
}
With that, the second argument can be omitted, and the caller can be simplified to:
GuardAgainst.Null(argument);
Think of the ? specifier on a type as meaning two things: 1) the value can be null before the call, and 2) the value can be null afterwards. Another way of writing it is [AllowNull, MaybeNull]. The absence of ? in a nullable context equally means [DisallowNull, NotNull]. In the case of your Null method, we end up with [AllowNull, NotNull] due to the manual specification of NotNull.

Ok, it looks like there is a really simple solution to it - the ! operator
You have to use it once after the guard, and then it considered to be not null:
public void Foo(string? argument)
{
GuardAgainst.Null(argument, nameof(argument));
var length = argument!.Length;
}

Consider this solution with the null-coalescing operator ??
The null-coalescing operator ?? returns the value of its left-hand
operand if it isn't null; otherwise, it evaluates the right-hand
operand and returns its result. The ?? operator doesn't evaluate its
right-hand operand if the left-hand operand evaluates to non-null.
public void Foo(string? argument)
{
string variable = argument ?? throw new ArgumentNullException(nameof(argument));
var length = argument.Length;
}
This solution is much cleaner in my opinion. You avoid inspecting GuardAgainst class and .Null() static method implemetation details.

Related

Is there a better way to deal with nullable return that depends on another value?

I have a method that returns a tuple (MyEnum, MyObject?). There is one and only one enum value that means MyObject is not null - all the others mean MyObject will be null (and the string will have a message).
Something along the lines of
(MyEnum response, Package? package) MyMethod()
{
if (Today == NiceDay)
{
return (MyEnum.Happy, new Package("I'm happy!"));
}
if (Today == RainyDay)
{
return (MyEnum.Pensive, null);
}
if (Today == SnowyDay)
{
return (MyEnum.Cold, null);
}
}
Obviously, if I try and use MyMethod().package.message, the compiler will warn me about the possibility of a null reference. I'm aware of the null-forgiving operator, but that kind of feels like a quick-fix.
Is there a way to "tell" the compiler to expect a value when MyMethod().response is MyEnum.Happy, and null when it is any other value, or is the null-forgiving operator the correct and expected way to deal with this situation?
Currently attributes for null-state static analysis interpreted by the C# compiler do not support neither specifying nullability of selected properties of returned value nor scenario when specific constant/enum value can be tied to nullability of parameters (except bool constants). The closest thing which can be done - return boolean value and tie out parameter to the return:
bool MyMethod(out MyEnum response, [NotNullWhen(true)] out Package? package)
{
string Today = "";
response = MyEnum.Happy;
package = new Package("");
return true;
}
Usage:
if(MyMethod(out _, out var packageValue))
{
Console.WriteLine(packageValue.ToString()); // no warning
}
So there are many ways to handle null/null warnings.
First
If you are 100% sure that the value cannot be null here you can use ! operator something like this package!.Message
Second
If You are not sure and there is only one condition in which the value can not be null its better to use the ternary operator like this var result = MyMethod().response == MyEnum.Happy ? MyMethod().package.message : string.Empty;
Third
you can use ?? operator like this var message = MyMethod().package?.message ?? "No message found"; or empty string according to your use case.
As a suggestion to resolve the issue, instead of the null have a static property in the Package class that defines an empty package:
class Package {
public static Package None => new Package { Message = "No package." };
}
In your code make the return for Package non-nullable and use
return (MyEnum.Pensive, Package.None);

default(T?) does not return null when T is a value type

I've come across the following phenomenon and am absolutely bamboozled. I'm using C# 10 with nullables enabled.
default(int?) returns null as expected. The following function, however, returns whatever default(T) is
public static T? ShouldReturnNull<T>()
{
return default(T?);
}
In the case of ShouldReturnNull<int>() we get 0. Shouldn't it also return null?
I have the following code in my program where this becomes an issue:
public T?[] FindKElements(...)
{
var result = new (T, double)?[k];
// ... populate result array,
// possibly including null values...
// return an array containing only the T part or null
return result.Select(e => e is null ? default(T?) : e.Value.Item1).ToArray();
}
Is there a way to keep it this generic but with proper nulls instead when T is a value type? The compiler won't let me use null in place of default(T?).
In the absence of a where T : struct constraint, T? does not mean Nullable<T>; it means "T, but note that it won't be null in the NRT sense" - and since NRT nulls never apply to your value-type scenario: it basically just means T; and the default value of a value-type T is not null (in any sense).
In the scenario where you need "null checking" that crosses both value-type and reference-type scenarios including support for value-types without a value, then the easiest approach is usually to forget about Nullable<T> and just track:
do I have a value (bool), and
what is the value (T)
separately, and explicitly; this could be via any of:
bool SomeMethod(out var T)
(HasValue: bool, Value: T) SomeMethod()
Maybe<T> SomeMethod()
(where Maybe<T> is just a custom struct that is composed of a bool HasValue and a T Value)
This is effectively creating something akin to Nullable<T>, but which applies to all values, regardless of type. Instead of checking for null, just check .HasValue first, and if true, assume that the value is meaningful.

How to deconstruction Nullable Tuple?

I am using C# 8.0 with NullableContextOptions (Nullable Reference) enabled.
I have a function with this signature:
public static (int line, string content)? GetNextNonEmptyLineDown(this IList<string> contents, int fromLine)
Basically, it returns the line and the content if there is non-empty line down, and returns if there is none.
The problem is I don't know how to deconstruct it. If I try:
var (firstLineIndex, firstLine) = content.GetNextNonEmptyLineDown(0);
I receive the 4 syntax errors:
So I can only use:
var lineInfo = content.GetNextNonEmptyLineDown(0);
var firstLineIndex = lineInfo.Value.line;
var firstLine = lineInfo.Value.content;
which destroys the purpose. lineInfo is of type struct<T> where T is (int line, string content)
Is there anyway to deconstruct a nullable tuple?
EDIT: after posting the question, it comes to my mind that it makes no sense to allow deconstructing of nullable tuple as the data type of the variables may not be determined. Here is my current fix but wonder if this is the best way:
var lineInfo = content.GetNextNonEmptyLineDown(0);
if (lineInfo == null)
{
throw new Exception();
}
var (firstLineIndex, firstLine) = lineInfo.Value;
From this question on nullable types:
Any Nullable is implicitly convertible to its T, PROVIDED that the entire expression being evaluated can never result in a null assignment to a ValueType.
So what you're looking for is to place at the right of the deconstruction a surely not-null expression. An elegant way to do this is:
var (firstLineIndex, firstLine) = lineinfo ?? default;
?? is the null-coalescing operator: it returns the value at its left if it is not null, the one at its right otherwise.
What we place at the right is the default operator, which nicely fits its return type to the surrounding expression.
Note that the use of default is mainly to please the compiler, you may not want that value to be actually used at runtime. You should still check for null beforehand.
If you want to deconstruct nullable tuple of value types into Nullable this works(DateTime example):
(DateTime start, DateTime end)? foo = null;
(DateTime? start, DateTime? end) = foo ?? ((DateTime?)null, (DateTime?)null);
Ugly as sin, but works... Could be prettied up with extension methods, but you'd pretty much need one for each arity of ValueTuple.

Why does pattern matching on a nullable result in syntax errors?

I like to use pattern-matching on a nullable int i.e. int?:
int t = 42;
object tobj = t;
if (tobj is int? i)
{
System.Console.WriteLine($"It is a nullable int of value {i}");
}
However, this results in the following syntax errors:
CS1003: Syntax error, ';',
CS1525: Invalid expression term ')',
CS0103: The name 'i' does not exist in the current context.
'i)' is marked with a red squiggly line.
The expression compiles when using the old operator is:
int t = 42;
object tobj = t;
if (tobj is int?)
{
System.Console.WriteLine($"It is a nullable int");
}
string t = "fourty two";
object tobj = t;
if (tobj is string s)
{
System.Console.WriteLine($#"It is a string of value ""{s}"".");
}
Also works as expected.
(I'm using c#-7.2 and tested with both .net-4.7.1 and .net-4.6.1)
I thought it had something to with operator precedence. Therefore, I have tried using parenthesis at several places but this didn't help.
Why does it give these syntax errors and how can I avoid them?
The type pattern in its various forms: x is T y, case T y etc, always fails to match when x is null. This is because null doesn't have a type, so asking "is this null of this type?" is a meaningless question.
Therefore t is int? i or t is Nullable<int> i makes no sense as a pattern: either t is an int, in which case t is int i will match anyway, or it's null, in which case no type pattern can result in a match.
And that is the reason why t is int? i or t is Nullable<int> i are not, and probably never will be, supported by the compiler.
The reason why you get additional errors from the compiler when using t is int? i is due to the fact that, e.g. t is int? "it's an int" : "no int here" is valid syntax, thus the compiler gets confused over your attempts to use ? for a nullable type in this context.
As to how can you avoid them, the obvious (though probably not very helpful) answer is: don't use nullable types as the type in type patterns. A more useful answer would require you to explain why you are trying to do this.
Change your code into:
int t = 42;
object tobj = t;
if (tobj is Nullable<int> i)
{
Console.WriteLine($"It is a nullable int of value {i}");
}
This produces the more helpful:
CS8116: It is not legal to use nullable type 'int?' in a pattern; use the underlying type 'int' instead (Could not find documentation about CS8116 to reference)
Others (user #Blue0500 at github ) have tagged this behaviour as a bug Roslyn issue #20156. Reacting to Roslyn issue #20156, Julien Couvreur from Microsoft has said he thinks it is by design.
Neal Gafter from Microsoft working on Roslyn has also said better diagnostics are wanted for use of nullable type is switch pattern.
So, the error message can be avoided by using:
int t = 42;
object tobj = t;
if (tobj == null)
{
Console.WriteLine($"It is null");
}
else if (tobj is int i)
{
Console.WriteLine($"It is a int of value {i}");
}
Except for issues when parsing tobj is int? i, this still leaves the question why is tobj is int? i or tobj is Nullable<int> i not allowed.
For anyone wondering how to actually use pattern matching with nullables, you can do so with a generic helper function, like so:
public static bool TryConvert<T>(object input, out T output)
{
if (input is T result)
{
output = result;
return true;
}
output = default(T);
// Check if input is null and T is a nullable type.
return input == null && System.Nullable.GetUnderlyingType(typeof(T)) != null;
}
This will return true if T is a nullable or non-nullable of the same type that input contains or if input is null and T is nullable. Basically works the same as normal, but also handles nullables.
Side note: Interestingly, from my testing, I found System.Nullable.GetUnderlyingType(typeof(T)) allocates 40 bytes of garbage every time it's called if T is nullable. Not sure why, seems like a bug to me, but that's potentially a hefty price to pay rather than just null-checking like normal.
Knowing that, here's a better function:
public static bool TryConvert<T>(object input, out T? output) where T : struct
{
if (input is T result)
{
output = result;
return true;
}
output = default(T?);
return input == null;
}

Rewrite HasValue to the ?? Operators

Is it safe to rewrite the following code:
bool b = foo.bar.HasValue ? foo.bar.Value : false;
to
bool b = foo.bar.Value ?? false;
where bar is the nullable type bool?
The easiest fix there is
bool b = foo.bar.GetValueOrDefault();
which is also actually cheaper than .Value as it omits the has-value check. It will default to default(T) , which is indeed false here (it just returns the value of the underlying T field, without any checks at all).
If you need a different default to default(T), then:
var value = yourNullable.GetValueOrDefault(yourPreferredValue);
What you want is:
bool b = foo.bar ?? false;
This is (surprisingly) safe and an intended use for the null-coalescing operator.
The ?? operator is called the null-coalescing operator and is used to define a default value for a nullable value types as well as reference types. It returns the left-hand operand if it is not null; otherwise it returns the right operand.
Source: http://msdn.microsoft.com/en-us/library/ms173224.aspx
In the case of Nullable<T>, it is functionally equivalent to Nullable<T>.GetValueOrDefault(T defaultValue).
The code:
bool b = foo.bar.Value ?? false;
Will cause a compiler-error, because you cannot apply the operator to value types, and Nullable<T>.Value always returns a value-type (or throws an exception when there is no value).
No - this is not safe.
The line:
bool b = foo.bar.Value ?? false;
will throw an InvalidOperationException if foo.bar has no value.
Instead use
var b = foo.bar ?? false;
Update - I just learned about .GetValueOrDefault(); from the other answers - that looks like a very good suggestion to use!
Update 2 - #ProgrammingHero's answer is also correct (+1 added!) - the line:
bool b = foo.bar.Value ?? false
actually won't compile - because of Error 50 Operator '??' cannot be applied to operands of type 'bool' and 'bool'
Nope.
The Value property returns a value if
one is assigned, otherwise a
System.InvalidOperationException is
thrown.
From: http://msdn.microsoft.com/en-us/library/1t3y8s4s%28v=vs.80%29.aspx
So if hasValue is false then you will get an exception thrown in your second one when you try to run it.
ffoo.bar ?? false would be more safer to use
bar.Value ?? false
has compilation error, because left operand of ?? operator should be of reference or nullable type.
Maybe you have a look at this article in Stackoverflow too - it is an elegant null checking in the style obj.IfNotNull(lambdaExpression) - returning the object you want if obj is not null otherwise just null (but without throwing an exception).
I used it with the Entity Framework if you're accessing a referenced entity, e.g.
var str=Entity1.Entity2.IfNotNull(x=>x.EntityDescription) ?? string.Empty
which returns EntityDescription contained in Entity2 which is referenced by Entity1 - or an empty string if any object Entity1 or Entity2 is null. Without IfNotNull you would get a long and ugly expression.
The extension method IfNotNull is defined there as follows:
public static TOut IfNotNull<TIn, TOut>(this TIn v, Func<TIn, TOut> f)
where TIn : class
where TOut: class
{
if (v == null) return null; else return f(v);
}
Update:
If you update the code to C# version 6.0 (.NET Framework 4.6 - but seems to support older frameworks 4.x too), there is a new operator available which makes this task easy: The "elvis" operator ?..
It works as follows:
var str=(Entity1.Entity2?.EntityDescription) ?? string.Empty
In this case, if Entity2 is null, evaluation stops and (...) becomes null - after which the ?? string.empty part replaces null by string.Empty. In other words, it works the same way as .IfNotNull(...) would.
foo.bar.Value represents the non-nullable value when there is one, and throws an InvalidOperationException if there’s no real value.

Categories

Resources