Как правильно определить постоянное значение в производном классе, используемом для проверки параметров в родительском конструкторе в C # - PullRequest
0 голосов
/ 08 июля 2019

Как правильно определить постоянное значение в производном классе для проверки параметра в родительском конструкторе.

Учитывая следующий пример:

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 (да, я понимаю предупреждение, я не спрашиваю об этом)

В моей логике (которая кажется ошибочной), VertexCount является особенностью класса Polygon (потому что фиксированный размер массивов как Edges, так и Vertices будет определяться в пределах Класс многоугольника) во время инициализации и изменяется в зависимости от производного класса (следовательно, должен быть определен в производном классе).

Производный класс НЕОБХОДИМО для определения значения VertexCount, поскольку он специфичен для производного класса, однако базовый класс не знает о производном классе. Это причина для абстрактного int, чтобы гарантировать, что производный класс переопределяет его. Кроме того, 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 ()' будет вызываться в контексте базового класса, а не производного класса (возможно, здесь будет работать решение с отражением)?

Таким образом, при всем этом альтернативой является отказ от конструктора в базовом классе, создание константы для VertexCount в производных классах, а также инициализация и установка всех значений в конструкторах производных классов. Похоже, что в производных классах будет много дублированного кода (возможно, не в приведенном выше примере, потому что технически я дублирую только 2 строки кода в каждой, но что происходит, когда конструктор становится тяжелее).

Каков «правильный» способ сделать это с наименьшим количеством дублирующегося кода?

1 Ответ

0 голосов
/ 08 июля 2019

Перечитав комментарии Питера Дунихо, я придумал этот рефакторинг, который, на мой взгляд, является гораздо лучшим решением.(Я думаю, что формулировка вопроса была немного двусмысленной / субъективной, потому что изначально логика была отключена, и я искал исправление в моей логике, а не точно знал, в чем была проблема и как ее решить)

Вопрос:

Как правильно определить постоянное значение в производном классе, используемом для проверки параметров в родительском конструкторе в C #

Краткий ответ,ты неОпределение константы в приведенном выше примере кода не является обязательным.Его не нужно хранить вообще, это не то, что нужно другим частям кода (в данном случае), это значение, которое необходимо только для проверки при создании экземпляра.

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

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

Другой целью, которую я пытался достичь (с помощью исходного примера), была ранняя проверка.Другими словами, если правильное количество вершин не передано для производного типа, я хочу остановить инициализацию до того, как любые другие шаги инициализации будут выполнены для недопустимого объекта.В данном примере пустой конструктор по умолчанию и проверка в производном конструкторе (или даже вызов метода проверки, содержащегося в базовом методе из производного конструктора) будет в порядке, не передавая вершины в базу, скорее проверяя, а затемустанавливая их в производном конструкторе.Однако ключом является ранняя проверка, что если базовый класс выполняет кучу вещей (более сложный класс), чтобы (предварительно) инициализировать объект перед тем, как перейти к производному классу.Код проверки действительно принадлежит перед любой обработкой (включая инициализацию).

Передача (связанного с проверкой) значения в базовый конструктор позволяет выполнить проверку в начале, чтобы предотвратить выполнение другого кода при проверке.не удается.

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...