【问题标题】:Is there a way to automaticly call all versions of an inherited method?有没有办法自动调用继承方法的所有版本?
【发布时间】:2010-12-27 01:04:40
【问题描述】:

我正在为 3D 建模程序编写插件。我有一个自定义类,它包装 3D 模型中的元素实例,然后从它包装的元素中派生它的属性。当模型中的元素发生变化时,我希望我的类根据新几何更新它们的属性。

在下面的简化示例中。我有类 AbsCurveBasd、Extrusion 和 Shell,它们都是相互派生的。这些类中的每一个都实现了一个 RefreshFromBaseShape() 方法,该方法根据类正在包装的当前 baseShape 更新特定属性。

我可以在 RefreshFromBaseShape() 的每个实现中调用 base.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
    }
}

【问题讨论】:

    标签: c# inheritance methods polymorphism virtual


    【解决方案1】:

    你做这件事的方式是一个非常典型的模式,它肯定没有什么问题——通常是这样做的。没有办法强制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
        }
    }
    

    【讨论】:

    • 啊,是的,我在想我可以做一些涉及事件或委托的事情。但这并没有让事情变得比使用 base.RefreshFromBaseShape() 更简单
    • 它并没有让它变得更简单。它只是保证没有派生类能够跳过其祖先提供的代码(尽管它仍然可以切断任何自己的后代)。
    【解决方案2】:

    我不确定您为什么需要一个单独的虚拟方法来执行此操作。为什么每个类不能在自己的构造函数中进行计算?在您的示例中, Area 将在 AbsCurveBased 构造函数中计算; Extrusion 构造函数中的体积和高度等。

    一般来说,从构造函数调用虚函数是不好的做法,因为子类在构造函数完成之前调用虚函数。所以子类中的方法正在运行,而对象只是部分通过构造。

    评论后更新

    在这种情况下,我将在每个类中都有一个私有 DoCalculate() 方法。这将由构造函数以及不再在构造函数中调用的 RefreshFromBaseShape() 调用。但它并不能减轻链接碱基调用的需要,这是一种正常的模式。

    【讨论】:

    • 我需要在实例化对象时计算属性,但如果基础形状发生变化,也可能在以后计算。
    猜你喜欢
    • 2022-11-09
    • 2019-09-10
    • 2011-12-06
    • 1970-01-01
    • 2011-01-14
    • 2011-02-16
    • 1970-01-01
    • 2013-09-20
    • 1970-01-01
    相关资源
    最近更新 更多