C#如何标记抽象class的属性不为空,因为在实现class构造函数时需要设置(CS8618)?

C# how to mark the property of the abstract class is not null because setting is required in implementing class constructor (CS8618)?

我有一个摘要 class 作为各种实现的基础 class。

让它看起来像这样:

public abstract class MyBase {

    public object Property1 { get; protected set; }

}

重要的是,Property1 的类型是 object 而不是 object?。 我希望完全清楚 Property1 在实现实例中永远不会为空。

假设典型的实现如下所示:

public sealed class MyImpl : MyBase {

    public MyImpl() {
        Property1 = new object();
    }

}

当然,在现实世界中,基础 class 中有很多属性,而且它们的类型多种多样,而不仅仅是 object。我可以将这些属性标记为 abstract,但是我必须在每个实现 class 中实现它们——这意味着更多的样板代码并没有真正的好处,对吧?

好的,但现在我收到一条警告,在退出构造函数之前属性必须具有非空值 - CS8618。当然我可以为我的基础 class 禁用警告,但是是否有更优雅的方法来指示编译器实现 class 必须将这些属性设置为非空值而不是禁用警告?

如果我收到的警告不是针对基础 class,而是针对实现,如果它没有设置其中一个属性,那就完美了。

正如我已经发现的那样 - 最新的 C# 编译器和 Visual Studio 有很多神奇的特殊属性可以改变可为 null 的警告行为,但我不知道去哪里找。

It is important, that the type of Property1 is object and not object?. I want it to be completely clear that the Property1 is never null in implementation instance.

只有 方法可以保证 MyBase 中的 protected 构造函数需要 Property1 [=55= 的值] 或 通过使 Property1 成为 abstract 属性.

在 .NET 中,当子class 被实例化时,首先它的超类型的构造函数运行完成,然后是下一个子类型的构造函数,依此类推。目前在 .NET 中没有规定超类型有一个在 most-derived-type 的构造函数运行之后运行的“post-constructor”(我希望有……),所以没有办法MyBase class 到 向编译器 证明 Property1 在构造对象时将处于有效状态。拥有 protected set 不是任何类型的可证明的保证(至少就编译器而言):它是 OOP 等同于 subclass 说“我 pinkie-swear 来设置它属性 在我的构造函数中!”你程序的其余部分必须信任那个口头承诺。

这就是自 C# 8.0 以来的可空注释如此重要且如此有用的原因:因为现在我们 no-longer 需要仅基于信任进行操作:我们现在可以让编译器 实际验证 field/auto-property 实际上分配给了 non-null 值,从而防止任何 NullReferenceException 每个人最喜欢的异常!)曾经被扔在第一位。

在这种情况下,非abstract 子class 是sealed 的事实对编译器来说无关紧要,因为您的MyBase 仍然可以单独派生一个永远不会设置 Property1.

的行为不端的 subclass

Of course in real world there are plenty of properties in the base class and they are of various types, not just object. I could mark the properties as abstract, but then I would have to implement them in each implementing class - that would mean more boilerplate code with no real benefit in that, right?

我理解必须复制+粘贴或手动 keystroke-in 重复的成员很烦人,但如果(重新)实现这些 abstract 成员表达了一些有意义的东西,那么这样做不是“样板”你的 business/domain 模型。

I get a warning that the properties must have non-null values before exiting constructor - CS8618. Of course I can disable the warning for my base class, but is there a more elegant way to instruct the compiler that the implementing class MUST set those properties to non-null values instead of disabling the warning?

只需使用 protected 构造函数。这正是他们的目的。

像这样:

public abstract class MyBase
{
    protected MyBase(object property1Value)
    {
        this.Property1 = property1Value ?? throw new ArgumentNullException(nameof(property1Value));
    }

    public object Property1 { get; }

}

这样所有子classes 都是安全的:

public sealed class Derived : MyBase
{
    public Derived()
        : base( GetProperty1FromAStaticFactory() )
    {

    }
}

public sealed class Derived : MyBase
{
    public Derived( Object p1Value )
        : base( p1Value  )
    {

    }
}