可空引用类型和 ToString() 重载
Posted
技术标签:
【中文标题】可空引用类型和 ToString() 重载【英文标题】:Nullable references types and ToString() overload 【发布时间】:2020-04-22 03:55:42 【问题描述】:请注意,这个问题是关于最新的 C# 8 nullable-references,我已通过以下 <Nullable>enable</Nullable>
声明在 csproj
文件中启用它。
考虑下面的简单代码
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
return Value.ToString();
行给了我一个 CS8603 可能的空引用返回 警告,我的问题实际上为什么会在这里?
我使用where T : struct, IComparable, IComparable<T>, IConvertible, IEquatable<T>, IFormattable
泛型约束来匹配数字类型,Value
实际上是值类型,而不是引用类型。 ToString()
也不会重载到任何值类型,Int32
的默认 implementation(例如)返回不可为空的 string
。 MSDN notes to inheritors 也是这么说的
您的
ToString()
覆盖不应返回Empty
或null
字符串。
编译器是否抱怨某种类型,它可以满足泛型约束并从ToString()
返回null
?
我可以通过使返回类型为空来避免警告
public override string? ToString()
return Value.ToString();
或使用空合并运算符
public override string ToString()
return Value.ToString() ?? "";
或通过 null-forgiving 运算符
public override string ToString()
return Value.ToString()!;
但是这些选项大多看起来像一个技巧,我正在寻找这种行为的解释,为什么会出现,设计或任何其他原因发生?除了上面的那些,还有什么方法可以避免这个警告?
顺便说一句,这个选项不起作用,警告仍然存在
[return: MaybeNull]
public override string ToString()
return Value.ToString();
我正在使用 .NET Core 3.1 和 VS 2019 16.4.2,但我认为这在这里并不重要。 提前感谢您的帮助!
【问题讨论】:
ToString
不应该返回空字符串,返回有意义的东西。
Roslyn 中似乎缺少位。向下滚动到 UPDATE 2019-10-08 (II):cezarypiatek.github.io/post/…
@Çöđěxěŕ 是的,我已经按照 msdn 的指南进行了操作。
@ZorgoZ 感谢更新,没看到这个更新和PR,其实和上面的msdn链接矛盾
我记得看到过关于object.ToString()
是否应该返回string?
或string
的讨论(虽然我现在找不到)。结论是,尽管指导原则是永远不要返回 null,但实际上存在很多可以返回 null 的代码(包括返回对其成员之一调用 ToString()
的结果的代码)。因此,让object.ToString()
返回string
会在大多数人的代码中引入很多警告。为了减少噪音,他们决定记录实际发生的情况,但如果您愿意,可以返回 string
。
【参考方案1】:
object.ToString()
的签名是:
public virtual string? ToString()
即对象的ToString()
方法被定义为返回一个可能为空的字符串。
Node.ToString()
的重载加强了这一要求,并承诺返回一个非空字符串。这可以。例如,Int32
会这样做(如您所述)。
然而,您的Node.ToString()
方法返回来自Value.ToString()
的值。我们刚刚看到这个ToString
方法(即object.ToString()
)可能会返回null
。因此编译器会警告您,如果Value.ToString()
返回null
,您的Node.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?
?请参阅this thread 以获得完整的讨论,但要点是有ToString
方法在野外确实返回null
,因为有些人不遵循你不应该遵循的指导方针返回null
或空字符串。
-
如果您引用的类型是在没有可空注释的情况下构建的,
object.ToString()
返回string?
的事实意味着您将收到警告,除非您检查null
。这可以保护您免受写得不好的 ToString
方法的影响。
如果您引用的类型是 使用 可空注释构建的,那么:
-
作者遵循指南,并将他们的
ToString
方法声明为返回string
。在这种情况下,编译器假定您不会得到null
。
作者明确没有遵循指南,并将他们的ToString
方法声明为返回string?
。在这种情况下,您必须检查 null
。
请注意,当您在 Visual Studio 中创建 ToString
的重载时,生成的方法会返回 string
(即使被重载的方法返回 string?
)。这会提示您遵循指南。
这里唯一的烦恼是当您处理泛型类型或已转换为object
的类型时。在这种情况下,编译器不知道对象的ToString
方法是否遵循准则。因为object.ToString
返回string?
,所以编译器会假设最坏的情况。如果您愿意,可以使用容错运算符 !
覆盖此假设。
【讨论】:
谢谢你的详细回答,有道理,我这里没有考虑object.ToString()
。但是为什么[return: MaybeNull]
在这种情况下不起作用?
@PavelAnikhouski MaybeNull
只影响方法的contract -- 它影响调用该方法的其他代码,但不影响方法内部的代码。 Compare here以上是关于可空引用类型和 ToString() 重载的主要内容,如果未能解决你的问题,请参考以下文章