在 C# 的父构造函数中用于验证参数的派生 class 中定义常量值的正确方法是什么

What is the correct way to define a constant value in a derived class used to validate parameters in a parent constructor in C#

为了验证父构造函数中的参数,在派生的 class 中定义常量值的正确方法是什么。

给定以下示例:

public abstract class Polygon
{
    protected abstract int VertexCount { get; }
    public LineSegment[] Edges { get { /* ... */ } }
    public Point2D[] Vertices { get { /* ... */ } }

    protected Polygon(Point2D vertices)
    {
        if(vertices.Length != VertexCount)
            threw new ArgumentOutOfRangeException(...);
    } 
}

public class Triangle: Polygon
{
    protected override int VertexCount { get; } = 3;
    public Triangle(Point2D[] vertices) : base(vertices){}
}

public class Quadrilateral: Polygon
{
    protected override int VertexCount { get; } = 4;
    public Quadrilateral(Point2D[] vertices) : base(vertices){}
}

显然,由于 Virtual member call in constructor(是的,我理解警告,我不是在问这个)

,上述内容无法按预期工作

在我的逻辑中(这似乎是有缺陷的),VertexCountPolygon class 的一个特征(因为 [=16] 的数组的固定大小=] 和 Vertices 将在初始化期间由多边形 class) 定义,并根据派生的 class 而变化(因此需要在派生的 class 中定义) .

派生classNEEDS来定义VertexCount的值,因为它是特定于派生的class,然而,base class 不知道派生的 class。这就是 'abstract int' 的原因,以确保派生的 class 覆盖它。此外,VertexCount 是一个 属性,因为字段不能是抽象的。但是,它也应该是一个常数(因为三角形的边总是 3 而四边形的边总是 4)。

最后,在基本构造函数中抛出的 Exception 消息应该类似于:

if(vertices.Length != VertexCount)
    threw new ArgumentOutOfRangeException(nameof(vertices),
        message: $"A {this.ToString()} requires exactly {VertexCount} vertices, received {vertices.Length}");

当然,'this.ToString()' 部分会在基础 class 的上下文中调用,而不是派生的 class (也许反射解决方案可以在这里工作)?

因此,对于所有这些,另一种方法是放弃基础 class 中的构造函数,在派生的 class 中为 VertexCount 创建一个常量,然后初始化和在派生的 classes 构造函数中设置所有值。这似乎在派生的 classes 中有很多重复的代码(也许不是在上面的例子中,因为从技术上讲我只是在每个代码中复制 2 行代码,但是当构造函数变得更重时会发生什么) .

用最少的重复代码执行此操作的'proper'方法是什么?

在 re-reading Peter Duniho 的评论之后,我想出了这个重构,我认为这是一个更好的解决方案。 (我认为问题的措辞有点 ambiguous/subjective 因为逻辑最初是错误的,我正在寻找对我的逻辑的更正而不是确切地知道问题是什么以及如何解决该问题)

问题:

What is the correct way to define a constant value in a derived class used to validate parameters in a parent constructor in C#

简短的回答,你不知道。没有必要在上面的示例代码中定义常量。它根本不需要存储,它不是代码的其他部分(在这种情况下)需要的东西,它是一个只需要在实例化时进行验证的值。

public class Triangle: Polygon
{
    public Triangle(Point2D[] vertices) : base(vertices, 3){}
}

public class Quadrilateral: Polygon
{
    public Quadrilateral(Point2D[] vertices) : base(vertices, 4){}
}

我试图完成的另一个目标(使用原始示例)是早期验证。换句话说,如果没有为派生类型传递正确数量的顶点,我想在对无效对象执行任何其他初始化步骤之前停止初始化。在给定的示例中,派生构造函数中的空默认构造函数和验证(或者甚至从派生构造函数调用基方法中包含的验证方法)会很好,而不是将顶点传递给基类,而是先验证然后再验证在派生的构造函数中设置它们。然而,关键是早期验证,如果基础 class 做了一堆东西(更复杂的 class)来(预)初始化对象,然后才到达派生 class。验证码确实属于任何处理(包括初始化)之前。

通过将(与验证相关的)值传递给基本构造函数,允许在开始时进行验证,以防止验证失败时其他代码运行。

protected Polygon(Point2D[] vertices, int expectedVerticesCount)
{
    // Don't bother initializing the object, the data is invalid!
    if(vertices.Length != expectedVerticesCount)
        throw new ArgumentOutOfRangeException( /* ... */);

    // The data is valid, proceed with initialization
    Vertices = vertices;
}