Есть ли способ автоматически вызывать все версии унаследованного метода? - PullRequest
0 голосов
/ 16 декабря 2009

Я пишу плагин для программы 3D-моделирования. У меня есть собственный класс, который оборачивает экземпляры элементов в 3D-модели и, в свою очередь, выводит его свойства из обертывания элемента. Когда элемент в модели изменяется, я хочу, чтобы мои классы обновили свои свойства на основе новой геометрии.

В упрощенном примере ниже. У меня есть классы AbsCurveBasd, Extrusion и Shell, которые все являются производными друг от друга. Каждый из этих классов реализует метод RefreshFromBaseShape (), который обновляет определенные свойства на основе текущего baseShape, который обертывает класс.

Я могу вызывать base.RefreshFromBaseShape () в каждой реализации RefreshFromBaseShape (), чтобы гарантировать, что все свойства обновлены. Но мне интересно, есть ли лучший способ, когда мне не нужно помнить, чтобы делать это в каждой реализации RefershFromBaseShape ()? Например, поскольку AbsCurveBased не имеет конструктора без параметров, код даже не будет компилироваться, если конструкторы не вызовут конструкторы базового класса.

public abstract class AbsCurveBased
{
    internal Curve baseShape;
    double Area{get;set;}

    public AbsCurveBased(Curve baseShape)
    {
        this.baseShape = baseShape;
        RefreshFromBaseShape();
    }

    public virtual void RefreshFromBaseShape()
    {
        //sets the Area property from the baseShape
    }
}


public class Extrusion : AbsCurveBased
{
    double Volume{get;set;}
    double Height{get;set;}

    public Extrusion(Curve baseShape):base(baseShape)
    {
        this.baseShape = baseShape;
        RefreshFromBaseShape();
    }

    public override void RefreshFromBaseShape()
    {
        base.RefreshFromBaseShape();
        //sets the Volume property based on the area and the height
    }
}


public class Shell : Extrusion
{
    double ShellVolume{get;set;}
    double ShellThickness{get;set;}

    public Shell(Curve baseShape): base(baseShape)
    {
        this.baseShape = baseShape;
        RefreshFromBaseShape();
    }

    public void RefreshFromBaseShape()
    {
        base.RefreshFromBaseShape();
        //sets this Shell Volume from the Extrusion properties and ShellThickness property
    }
}

Ответы [ 2 ]

0 голосов
/ 16 декабря 2009

Я не уверен, почему вам нужен отдельный виртуальный метод для этого. Почему каждый класс не может выполнять свои вычисления в своем собственном конструкторе? В вашем примере Area будет рассчитываться в конструкторе AbsCurveBased; Объем и высота в конструкторе Extrusion и т. Д.

В общем, плохая практика - вызывать виртуальную функцию из конструктора, поскольку виртуальная функция вызывается в подклассах до завершения их конструктора. Таким образом, метод в подклассе выполняется, пока объект только частично находится в процессе построения.

Обновление после комментария

В этом случае у меня будет частный метод DoCalculate () в каждом из классов. Это будет вызываться конструкторами, а также RefreshFromBaseShape (), который больше не будет вызываться в конструкторах. Тем не менее, это не избавляет от необходимости связывать базовые вызовы, что является нормальным паттерном.

0 голосов
/ 16 декабря 2009

То, как вы это делаете, является очень типичным паттерном, и в этом нет ничего плохого - так обычно делают подобные вещи. Невозможно заставить этот вызов base предположить, что когда и где это происходит, всегда зависит от разработчика. Если вы действительно заинтересованы в его форсировании, вам следует рассмотреть возможность использования защищенного события:

public abstract class AbsCurveBased
{
    internal Curve baseShape;
    double Area{get;set;}

    public AbsCurveBased(Curve baseShape)
    {
        this.baseShape = baseShape;
        RefreshFromBaseShape();
    }

    public void RefreshFromBaseShape()
    {
        //sets the Area property from the baseShape
        ...

        // call child handlers
        var handler = RefreshingFromBaseShape;
        if (handler != null)
            handler();
    }

    protected event Action RefreshingFromBaseShape;
}

public class Shell : Extrusion
{
    double ShellVolume{get;set;}
    double ShellThickness{get;set;}

    public Shell(Curve baseShape): base(baseShape)
    {
        this.RefreshingFromBaseShape += RefreshingFromBaseShapeHandler;

        this.baseShape = baseShape;
        RefreshFromBaseShape();
    }

    private void RefreshingFromBaseShapeHandler()
    {
        //sets this Shell Volume from the Extrusion properties and ShellThickness property
    }
}

Таким образом, любой класс в цепочке наследования контролирует только своих обработчиков и не может отменить регистрацию обработчиков своих предков или вставить их в цепочку до того, как предки вставят свои.

Однако для вашего конкретного случая это кажется слишком большой сложностью, ничего не стоящей.

Кроме того, если RefreshFromBaseShape вызывается только из конструктора, то, возможно, он должен быть параметром этого конструктора. Рассмотрим:

public abstract class AbsCurveBased
{
    internal Curve baseShape;
    double Area{get;set;}

    public AbsCurveBased(Curve baseShape)
    {
        this.baseShape = baseShape;

        //sets the Area property from the baseShape
    }

    protected AbsCurveBased(Curve baseShape, Action refreshFromBaseShape):
        this(baseShape)
    {
        refreshFromBaseShape();
    }
}

public class Shell : Extrusion
{
    double ShellVolume{get;set;}
    double ShellThickness{get;set;}

    public Shell(Curve baseShape):
        base(baseShape, RefreshFromBaseShape)
    {
    }

    protected Shell(Curve baseShape, Action refreshFromBaseShape):
        this(baseShape)
    {
        refreshFromBaseShape();
    }

    private void RefreshFromBaseShape()
    {
        //sets this Shell Volume from the Extrusion properties and ShellThickness property
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...