(The following answer is applicable as of Visual Studio 16.6/.NET Core 3.1)
You should use [MaybeNullWhen(false)] instead of [NotNullWhen(true)] in this scenario.
public bool TryGetValue(string subnet, [MaybeNullWhen(false)] out TValue value)
{
if (!TryFindNode(subnet, out var node))
throw new InvalidOperationException();
if (TValueIsDefault(node.Value) == false)
{
value = node.Value;
return true;
}
value = default;
return false;
}
Explanation:
The key point to understand is that applying a [NotNullWhen(true)] attribute does not imply [MaybeNullWhen(false)].
In your sample, it is permitted to supply a non-nullable reference type such as string as a type argument for TValue. This would produce the following method:
public bool TryGetValue(string subnet, [NotNullWhen(true)] out string value)
{
if (!TryFindNode(subnet, out var node))
throw new InvalidOperationException();
if (TValueIsDefault(node.Value) == false)
{
value = node.Value;
return true;
}
value = default; // Wait, default(string) is null! We can't do this!
return false;
}
What you probably want to do is instead of telling your caller the method will return "not null when true", tell them it "may return null when false". Then the type argument to your containing type TreeNode<TValue> will inform whether the caller should expect a non-null value when this method returns true. Thus the instantiation looks like:
public bool TryGetValue(string subnet, [MaybeNullWhen(false)] out string value)
{
if (!TryFindNode(subnet, out var node))
throw new InvalidOperationException();
if (TValueIsDefault(node.Value) == false)
{
value = node.Value;
return true;
}
value = default; // ok
return false;
}
Since the target of the assignment has MaybeNullWhen, the compiler permits you to assign null to it. We simply check in your return statement that the null-state of the variable is compatible with the return value. For example, if we changed return false to return true above, you would get a nullability warning.