问题
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?
回答1:
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);
来源:https://stackoverflow.com/questions/58279704/specify-notnull-if-method-returns-at-all