Open alrz opened 4 years ago
Had a situation like this last week, but I didn't write it down. :-(
I have a use case:
public static T Min<T>([AllowNull] T left, [AllowNull] T right)
where T : IComparable<T>
=> left == null ? (right == null ? default : (right.CompareTo(left) > 0 ? left : right)) : (left.CompareTo(right) < 0 ? left : right);
If either left
or right
is null the return value may be null, depending on the implementation of IComparable<T>
(although the spec for IComparable<T>
says that if either one is null the return value is null.)
@wilhelmzapiain That method would confuse me. I'd expect Enumerable.Min semantics: "the min of everything that isn't null"
Alternate option: https://github.com/dotnet/csharplang/discussions/6981
@wilhelmzapiain That method would confuse me. I'd expect Enumerable.Min semantics: "the min of everything that isn't null"
Maybe but if you want to write a null-safe Plus operator in analogy to SQL semantics, you have a perfectly valid example for this issue.
Either a solution with...
[return: MayBeNullIfNull(nameof(left)), MayBeNullIfNull(nameof(right))] // with OR semantics
public static Value operator +(Value? left, Value right) {}
... or ...
[return: NotNullIfNotNull(nameof(left), nameof(right))] // with AND semantics
public static Value? operator +(Value? left, Value right) {}
... would be highly appreciated also for our project where we have such operators used in really many places and refactoring all of them out to fully apply nullable reference types would be hell.
I just encountered this when trying to update some operators.
public static Measure? operator *(Measure? left, Measure? right)
{
if (left == null || right == null)
{
return null;
}
return new Measure(/*multiply left and right*/);
}
IMO MayBeNullIfNull
isn't entirely clear.
There's no concrete guarantee that the return will or won't be null based on the input nullability.
It would be clearer as [return: NullIfAnyNull(nameof(left), nameof(right))]
-- this says that the return WILL BE null if any are null.
AND semantics are already established for this,, but could use a clearer name:
[return: NotNullIfAllNotNull(nameof(left), nameof(right))]
IMO
MayBeNullIfNull
isn't entirely clear. There's no concrete guarantee that the return will or won't be null based on the input nullability. It would be clearer as[return: NullIfAnyNull(nameof(left), nameof(right))]
-- this says that the return WILL BE null if any are null.
You usually don't want to know when exactly a return value WILL be null but when the return value will surely be NOT null as only then, null checks in calling code can be omitted.
Currently multiple
NotNullIfNotNull
behaves as if they are being OR'edIf this is the intended behavior, a possible solution is to permit multiple arguments on the attribute
So that we get a warning if either of arguments are possible nulls.