c# - 可空引用类型和 ToString() 重载

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

请注意,这个问题是关于最新的 C# 8 nullable-references ,我已经在 csproj 中启用了它文件由以下 <Nullable>enable</Nullable>声明。

考虑下面的简单代码

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() 也没有过载。对于任何值类型,默认 implementation对于 Int32 (例如)返回不可为空的 string 。 MSDN notes to inheritors还说

Your ToString() override should not return Empty or a null string.

编译器是否提示某些类型可以满足泛型约束并返回 null来自ToString()

我可以通过使返回类型可为空来避免警告

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,但我认为这在这里并不重要。 预先感谢您的帮助!

最佳答案

object.ToString() 的签名是:

public virtual string? ToString()

即对象的ToString()方法被定义为返回一个可能为空的字符串。

您的 Node.ToString() 重载严格了这一要求并 promise 返回非空字符串。这可以。 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() ?? "";抑制警告: if Value.ToString()返回null ,该代码将确保 Node.ToString()没有返回null .

<小时/>

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

想要 promise 您的 Node.ToString()方法永远不会返回null ?如果是这样,您需要弄清楚如果 Value.ToString() 该怎么办返回null .

否则,最好遵循既定模式,并说您的 Node.ToString()方法可能返回null .

<小时/>

为什么 object.ToString()返回string? ?请参阅this thread进行完整的讨论,但要点是有 ToString do 返回 null 的野外方法,因为有些人不遵循“你永远不应该回来”的准则 null或空字符串。

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

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

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

关于c# - 可空引用类型和 ToString() 重载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59581046/

相关文章:

c# - CallerArgumentExpression 始终为空

c# - 将 float 转换为字符串。代替 ,

c# - 如何在两台计算机之间建立TCP通信

c# - 如何在IIS中下载文件?

c# - MVC - 使用 RedirectToAction() 传递数据

asp.net-mvc - .NET Core 带密码的分布式 Redis 缓存

twitter-bootstrap - 在 .Net Core Angular 模板中使用 webpack 编译 bootstrap scss

c# - MySql长时间运行的查询在Docker .NET Core上失败:尝试读取流的末尾/预期读取4个 header 字节,但仅收到0

c# - c#8 中的公共(public)接口(interface)成员

c# - 如何避免不相关的可空警告(没有显式抑制)