Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specify NotNull If Method Returns At All

I'm using the new nullable reference types from C# 8 and I was wondering if it is possible to indicate that a parameter passed in is not null if the method returns at all.

I've found [NotNullIf] and [DoesNotReturnIf] but these appear to trigger off a method's return value and a specific bool parameter respectively.

Here is what I currently have:

public static bool RaiseIfNull([NotNullWhen(true)] object? thing) => RaiseIf(() => thing is null);
public static bool RaiseIf(Func<bool> predicate)
{
  if (predicate()) throw new HttpException(400);
  return true;
}

This seems good, but then when I call it - I still see warnings. (I also tried RaiseIfNull([NotNullIfNotNull("thing")] object? thing) and that didn't work.)

[HttpPost("{id}")]
public async Task Etc(string id, [FromBody] Dto data)
{
  HttpException.RaiseIfNull(data?.property);
  await DoEtc(id, data.property)); // warning here
}

Am I missing something obvious?

like image 716
jjnguy Avatar asked Oct 08 '19 03:10

jjnguy


People also ask

What is NotNull in C#?

NotNull: A nullable field, parameter, property, or return value will never be null. MaybeNullWhen: A non-nullable argument may be null when the method returns the specified bool value. NotNullWhen: A nullable argument won't be null when the method returns the specified bool value.

Why use@ nullable?

The @Nullable annotation helps you detect: Method calls that can return null. Variables (fields, local variables, and parameters), that can be null.

What is null state analysis?

Null-state analysis tracks the null-state of references. This static analysis emits warnings when your code may dereference null . You can address these warnings to minimize incidences when the runtime throws a System. NullReferenceException. The compiler uses static analysis to determine the null-state of a variable.


1 Answers

Using a normal null-check

First of all, RaiseIfNull doesn't offer something more than :

var value=data?.property ?? new HttpException(400);

Which the compiler can recognize. RaiseIfNull on the other hand hides what's actually going on. This code doesn't produce a warning:

class D
{
    public string? property{get;set;}
}


D? data=null;
var value=data?.property ?? throw new HttpException(400);
Console.WriteLine(value.Length);

Unconditional Post-Condition and generics

That said, the the correct argument to use is NotNull - after the method executes, the parameter is not null even if the type itself is nullable. That attribute can be applied to :

  • Parameters
  • Properties
  • Fields and
  • Return values

The methods can be made generic too, to avoid boxing structs into objects. To do that we need to specify whether the type is a class or struct, because the resulting concrete types are very different - a string? is still a string while an int? is a Nullable<int> :

public static bool RaiseIfNull<T>([NotNull] T? thing) 
    where T:class
    => RaiseIf(() => thing is null);

public static bool RaiseIfNull<T>([NotNull] T? thing) 
    where T:struct
    => RaiseIf(() => thing is null);

Given those methods, the following code won't generate a warning either :

D? data=null;
RaiseIfNull(data?.property);
Console.WriteLine(data.property.Length);

Finally, we can get rid of the return value:

public static void RaiseIfNull<T>([NotNull] T? thing) 
    where T:class
    => RaiseIf(() => thing is null);

public static void RaiseIfNull<T>([NotNull] T? thing) 
    where T:struct
    => RaiseIf(() => thing is null);
like image 60
Panagiotis Kanavos Avatar answered Nov 07 '22 17:11

Panagiotis Kanavos