【问题标题】:Non-nullable readonly references and empty private default constructor in C# result in warning CS8618C# 中不可为空的只读引用和空的私有默认构造函数导致警告 CS8618
【发布时间】:2020-10-30 15:26:21
【问题描述】:

我的一些 C# 类在设计上没有默认构造函数。我习惯于声明一个空的私有默认构造函数,以防止我的类的客户端使用编译器提供的默认构造函数。

现在我在我的 C# 项目 (#nullable enable) 中启用了不可为空的引用,但这不再有效,我参考下面的 CS8618 警告:

#nullable enable

public sealed class Operation
{
    private readonly CancellationTokenSource _cts;

    private Operation()
    {
        // Warning CS8618 Non-nullable field '_cts' is uninitialized. 
        // Consider declaring the field as nullable.
       
        //_cts = null!;
    }

    public Operation(CancellationToken token)
    {
        _cts = CancellationTokenSource.CreateLinkedTokenSource(token);
    }

    ...
}

看来,我现在要么必须初始化所有不可为空的字段(我想是使用 null-forgiving null!?),要么完全删除非默认构造函数。解决这个问题的优雅方法是什么?

更新以解决 cmets:

也许我不够清楚,所以我会尝试解决这个问题。我很清楚,如果我没有明确声明,C# 会提供一个默认的public 无参数构造函数。这正是我声明它并使其成为private 的原因。 防止使用无参数构造函数构造Operation 类型的对象。 我还有一个工厂类:

public static class OperationFactory
{
    public static Operation* CreateOperation(CancellationToken token)
    {
        return new Operation(token);
    }
}

我班的客户可以这样做:

var operation = new Operation(token)

var operation = OperationFactory.CreateOperation(token)

但他们不允许这样做:

// this would create an object in invalid state
var operation = new Operation(); 

所以以前,要强制执行这些规则,我只需声明一个空的 private parameterless constructor: private Operation() {}

现在有了#nullable enable,我不能再有一个 empty 私有构造函数了。我现在必须在私有构造函数(仍然从未使用过,所以这是一个缺点)和公共构造函数中初始化所有不可为空的引用字段。

由于本质上从未使用私有构造函数,我认为可能有一种优雅的方法可以避免重复此类初始化代码。有吗?

【问题讨论】:

  • 如果只删除默认构造函数会怎样?
  • 不幸的是,要求最优雅的方式是徒劳的,因为这变成了一个时尚感问题。找出完成任务的方法并选择你最喜欢的
  • 请先解释一下为什么要添加无参数构造函数。当您提供任何构造函数时,编译器将不再生成默认的无参数构造函数,并且无法使用 new 实例化该类,除非使用您提供的显式构造函数之一。您的问题似乎是代码中有一个构造函数的直接结果,它无论如何都不应该有。完全不清楚为什么这个问题需要解决。
  • @PeterDuniho,我首先添加了private 无参数构造函数,因此我的库的用户无法选择以这种方式创建我的类的实例。我希望他们始终使用需要参数的构造函数。

标签: c#


【解决方案1】:

这里似乎对文档所述内容存在误解。

具体来说,the documentation 声明:

如果您没有为您的类提供构造函数,C# 默认会创建一个构造函数,用于实例化对象并将成员变量设置为 C# 类型的默认值一文中列出的默认值。

注意它没有说的是:

如果您没有为您的类提供 无参数 构造函数,...

如果你声明任何构造函数,编译器不会添加无参数的构造函数。

因此,您的类可以更改为此,只需删除私有无参数构造函数:

public sealed class Operation
{
    private readonly CancellationTokenSource _cts;

    public Operation(CancellationToken token)
    {
        _cts = CancellationTokenSource.CreateLinkedTokenSource(token);
    }

    ...
}

因为你有一个构造函数,public Operation(CancellationToken token),编译器不会添加一个无参数的构造函数,你会阻止任何人这样做:

new Operation()

另一方面,如果您确实需要为您自己的代码使用无参数构造函数,那么编译器是正确的。 _cts 字段将保留其默认值 null。在这种情况下,您必须弄清楚如何处理该 null,并可能告诉编译器“您是对的,我会这样标记它”以消除警告。

【讨论】:

  • 一个很好的答案,谢谢!并且来自始终提供默认构造函数的 C++,多年来我认为 C# 也是如此......我生活和学习:)
  • 在旁注中,我正在寻找类似[System.Diagnostics.CodeAnalysis.SuppressMessage("Compiler", "CS8618")] 的东西来应用于该私有构造函数,但这没有用。我想如果我真的需要,#pragma warning disable CS8618 将是最好的选择,除了进行实际的初始化。
猜你喜欢
  • 2020-07-28
  • 1970-01-01
  • 2013-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-30
  • 2020-05-04
  • 2020-05-19
相关资源
最近更新 更多