【问题标题】:Call abstract method from abstract class constructor从抽象类构造函数调用抽象方法
【发布时间】:2014-01-23 09:20:49
【问题描述】:

我想问一下有什么风险如下:

abstract public class HtmlTemplateBuilder
{
    HtmlSource source;

    protected HtmlTemplateBuilder()
    {
        LoadTemplates();
    }

    public abstract void LoadTemplates();
}

风险是如果派生类从派生类派生:

DerivedClass2 -> #DerivedClass1 -> HtmlTemplateBuilder

这可以通过密封#DerviedClass1 来解决,但是实现此功能是否有更多风险或更好的做法?

谢谢

【问题讨论】:

  • 您在示例中看到了什么风险?你能说得更具体些吗?
  • Resharper 说(我明白为什么):构造函数中的虚拟成员调用。

标签: c# oop abstract-class


【解决方案1】:

这种模式让我印象深刻的情况如下:在以后的某个阶段,你想添加一个专门的HtmlTemplateBuilder,它可以根据类本身未知的一些标准加载不同的模板(也许你决定想要一些一年中特定日期的酷模板)。那就是:

public class SpecialHtmlTemplateBuilder : HtmlTemplateBuilder
{
    private bool someCondition;

    public override void LoadTemplates()
    {
        if (someCondition)
        {
            LoadTemplatesSet1();
        }
        else
        {
            LoadTemplatesSet2();
        }
    }
}

但是你要如何将someCondition 传递给班级呢?以下将不起作用:

public class SpecialHtmlTemplateBuilder : HtmlTemplateBuilder
{
    private bool someCondition;

    public SpecialHtmlTemplateBuilder (bool someCondition)
    {
        this.someCondition = someCondition;
    }

    // ...
}

因为this.someCondition 的赋值将在 调用基本构造函数之后完成,即 LoadTemplates() 被调用之后。请注意,密封派生类不能解决这个问题。

解决这个问题的方法就像@Rahul Misra 描述的那样:添加一个显式的Initialize 方法并在构造函数之后调用它。

【讨论】:

    【解决方案2】:

    看看这个链接,它用简单易懂的例子解释了危险

    https://blogs.msmvps.com/peterritchie/2012/04/25/virtual-method-call-from-constructor-what-could-go-wrong/

    我会从构造函数中移除对 LoadTemplates 的调用,并在实际需要加载和使用模板时对其调用 Initialise。

    abstract public class HtmlTemplateBuilder
    {
        HtmlSource source;
        object locker = new object();
        private bool initialised;
    
        protected HtmlTemplateBuilder()
        {
        }
    
        protected void Initialise()
        {
            lock (locker)
            {
                if(initialised)
                {
                    LoadTemplates();
                    initialised = true;
                }
            }
        }
    
        public abstract void LoadTemplates();
    }
    

    【讨论】:

    • 这或多或少是我解释的风险,在我的示例中,实现的责任在于派生类。我要求提供没有风险的更好/替代解决方案。
    • 我会延迟加载模板。它不仅降低了将来有人可能通过更改您的类来重新引入风险的风险,而且还使对象保持轻量级,直到实际使用为止。在您需要加载模板的第一个点,通过在布尔标志中维护状态来检查它们是否已加载,否则加载。
    • 提供的链接已损坏。这有效:blogs.msmvps.com/peterritchie/2012/04/25/…
    • 谢谢拉吉。我已经修复了链接。
    猜你喜欢
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    • 2020-06-18
    • 1970-01-01
    • 1970-01-01
    • 2014-08-09
    • 2021-11-24
    • 1970-01-01
    相关资源
    最近更新 更多