【问题标题】:Nullable references types and ToString() overload可空引用类型和 ToString() 重载
【发布时间】: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&lt;T&gt;, IConvertible, IEquatable&lt;T&gt;, IFormattable 泛型约束来匹配数字类型,Value 实际上是值类型,而不是引用类型。 ToString() 也不会重载到任何值类型,Int32 的默认 implementation(例如)返回不可为空的 string。 MSDN notes to inheritors 也是这么说的

您的ToString() 覆盖不应返回Emptynull 字符串。

编译器是否抱怨某种类型,它可以满足泛型约束并从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

标签: c# .net-core c#-8.0 nullable-reference-types


【解决方案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 或空字符串。

  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?,所以编译器会假设最坏的情况。如果您愿意,可以使用容错运算符 ! 覆盖此假设。

【讨论】:

  • 谢谢你的详细回答,有道理,我这里没有考虑object.ToString()。但是为什么[return: MaybeNull] 在这种情况下不起作用?
  • @PavelAnikhouski MaybeNull 只影响方法的contract -- 它影响调用该方法的其他代码,但不影响方法内部的代码。 Compare here
猜你喜欢
  • 2012-08-01
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 2011-03-10
  • 1970-01-01
  • 1970-01-01
  • 2020-01-24
  • 1970-01-01
相关资源
最近更新 更多