Nullable references types and ToString() overload 可空引用类型和ToString()重载

huangapple go评论81阅读模式
英文:

Nullable references types and ToString() overload

问题

The line return Value.ToString(); gives you a CS8603 Possible null reference return warning because the C# 8 nullable reference feature is designed to ensure safety against null references. Even though your Value is of a value type, the compiler still warns you because it doesn't know for sure that Value will never be null.

The warning occurs because the compiler cannot guarantee that Value won't be null, even though it's a value type. The compiler doesn't treat value types as non-nullable by default because there are scenarios where value types can be assigned null values. For example, if T were a nullable value type, Value could potentially be null.

To avoid the warning, you can use the options you've mentioned, such as making the return type nullable (string?) or using null-coalescing or null-forgiving operators. These options provide a way to handle the potential null reference safely.

The [return: MaybeNull] attribute doesn't work in this case because it's typically used to indicate that a method may return null, but in your case, the warning is about the property Value, which is accessed within the ToString() method.

In summary, the warning is by design to promote null safety in C# 8 nullable reference types, and the options you've mentioned are the common ways to handle it. There isn't a way to completely eliminate the warning without addressing the potential null reference explicitly.

英文:

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 return Empty or a null 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!

答案1

得分: 15

object.ToString() 的签名是:

public virtual string? ToString()

也就是说,objectToString() 方法被定义为返回一个可能为 null 的字符串。

你的 Node.ToString() 的重载加强了这个要求,并承诺返回一个非 null 的字符串。这是可以的。例如,Int32 就是这样做的(正如你所指出的)。

然而,你的 Node.ToString() 方法返回了 Value.ToString() 的值。我们刚刚看到这个 ToString 方法(即 object.ToString())可能返回 null。因此,编译器警告你的 Node.ToString() 方法可能会意外地返回 null,如果 Value.ToString() 返回 null 的话。

这就解释了为什么声明 Node.ToString() 如下:

public override string? ToString()

会抑制警告:你现在声明你的 Node.ToString() 方法可能会返回 null,所以如果 Value.ToString() 返回 null,然后你返回这个值就不是问题了。

这也解释了为什么写 return Value.ToString() ?? "" 会抑制警告:如果 Value.ToString() 返回 null,这段代码将确保 Node.ToString() 不会返回 null

如何最好地解决这个问题?由你决定。

你是否承诺你的 Node.ToString() 方法永远不会返回 null?如果是这样,那么你需要想办法处理 Value.ToString() 返回 null 的情况。

否则,最好还是遵循已有的模式,声明你的 Node.ToString() 方法可能会返回 null

为什么 object.ToString() 返回 string??请参阅 此线程 进行详细讨论,但要点是,有些人不遵循不返回 null 或空字符串的指导原则,所以有野生的 ToString 方法会返回 null

  1. 如果你引用的类型没有带有可空注释构建,那么 object.ToString() 返回 string? 意味着除非你检查 null,否则你将会收到警告。这可以保护你免受糟糕编写的 ToString 方法的影响。
  2. 如果你引用的类型带有可空注释构建,那么要么:
    1. 作者遵循了指南,声明他们的 ToString 方法返回 string。在这种情况下,编译器会假设你不会得到 null
    2. 作者明确不遵循指南,声明他们的 ToString 方法返回 string?。在这种情况下,你被迫检查 null

请注意,当你在 Visual Studio 中创建 ToString 的重载时,生成的方法返回 string(即使被重载的方法返回 string?)。这会提示你遵循指南。

唯一的烦恼是当你处理泛型类型或将类型转换为 object 时。在这种情况下,编译器不知道对象的 ToString 方法是否遵循指南。因为 object.ToString 返回 string?,编译器会假定最坏的情况。如果你愿意,你可以使用 null-forgiving 操作符 ! 来覆盖这个假设。

英文:

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.

  1. If you are referencing a type which was built without nullable annotations, the fact that 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.
  2. If you a referencing a type which was built with nullable annotations, then either:
  3. The author followed the guidelines, and declared their ToString method as returning string. In this case, the compiler assumes that you won't get null.
  4. The author explicitly did not follow the guidelines, and declared their 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 !.

huangapple
  • 本文由 发表于 2020年1月3日 23:18:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/59581046.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定