Please, note that this question is about the latest C# 8 nullable-references, I've enabled it in csproj
file by the following <Nullable>enable</Nullable>
declaration.
Consider the following simple code
class SortedList<T> where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
{
private Node? _node;
private readonly IComparer<T> _comparer = Comparer<T>.Default;
class Node
{
public Node(T value)
{
Value = value;
}
public T Value { get; }
public Node? Next { get; set; }
public override string ToString()
{
return Value.ToString();
}
}
//rest of code, that isn't important
}
The line return Value.ToString();
gives me a CS8603 Possible null reference return warning and my question actually why is it here?
I'm using the where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
generic constraint to match a numeric types, Value
is value type actually, not the reference one. There is also no overload of ToString()
to any value type, the default implementation for Int32
(for example) returns non-nullable string
. MSDN notes to inheritors is also saying that
Your
ToString()
override should not returnEmpty
or anull
string.
Does the compiler complaining about some type, which can satisfy the generic constraint and return null
from ToString()
?
I can avoid the warning by making a return type nullable
public override string? ToString()
{
return Value.ToString();
}
or by using the null-coalescing operator
public override string ToString()
{
return Value.ToString() ?? "";
}
or by null-forgiving operator
public override string ToString()
{
return Value.ToString()!;
}
But these options look like a tricks mostly, I'm looking for explanation of this behavior, why is there, by design or any other reasons were take place? Is there any ways to avoid the warning, except those above?
Btw, this option doesn't work, warning still in place
[return: MaybeNull]
public override string ToString()
{
return Value.ToString();
}
I'm using .NET Core 3.1 and VS 2019 16.4.2, but I don't think it's really important here. Thanks in advance for help!
The signature for object.ToString()
is:
public virtual string? ToString()
That is, object's ToString()
method is defined as returning a string which may be null.
Your overload of Node.ToString()
tightens this requirement and promises to return a non-null string. This is fine. Int32
does this, for example (as you noted).
However, your Node.ToString()
method returns the value from Value.ToString()
. We just saw that this ToString
method (i.e. object.ToString()
) might return null
. Therefore the compiler's warning you that your Node.ToString()
method might inadvertently return null
, if Value.ToString()
returns null
.
This explains why you found that declaring Node.ToString()
as:
public override string? ToString()
suppressed the warning: you're now declaring that your Node.ToString()
method might return null
, so it's not a problem if Value.ToString()
returns null
and you then return this value.
It also explains why writing return Value.ToString() ?? "";
suppressed the warning: if Value.ToString()
returned null
, that code would ensure that Node.ToString()
did not return null
.
How best to fix this? You decide.
Do you want to promise that your Node.ToString()
method never returns null
? If so, you'll need to figure out what to do if Value.ToString()
returns null
.
Otherwise, it's probably best to follow the established pattern, and say that your Node.ToString()
method might return null
.
Why does object.ToString()
return string?
? See this thread for a full discussion, but the gist is that there are ToString
methods in the wild which do return null
, because some people don't follow the guideline that you should never return null
or an empty string.
object.ToString()
returns string?
means that you'll be given a warning unless you check for null
. This protects you from a badly-written ToString
methods.ToString
method as returning string
. In this case, the compiler assumes that you won't get null
.ToString
method as returning string?
. In this case, you're forced to check for null
.Note that when you create an overload of ToString
in Visual Studio, the generated method returns string
(even if the method being overloaded returns string?
). This prompts you to follow the guidelines.
The only annoyance here is when you're dealing with a generic type, or a type which has been cast to object
. In this case, the compiler doesn't know whether the object's ToString
method is following the guidelines or not. Because object.ToString
returns string?
, the compiler assumes the worst. You can override this assumption if you wish with the null-forgiving operator !
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With