【问题标题】:Construct derived-class properties without virtual member call in constructor在构造函数中构造派生类属性而不调用虚拟成员
【发布时间】:2019-05-06 01:10:08
【问题描述】:

我有一些嵌套类BaseOuterBaseInner,以及子类DerivedOuterDerivedInnerBaseOuter 具有属性BaseInner baseInner;,当我实例化DerivedOuter 时,我希望baseInner 属性的运行时类型为DerivedInner

我首先像下面这样解决了这个问题,使用虚拟初始化程序来实例化baseInner,它在DerivedOuter 中被覆盖。这使我可以在各自的初始化程序中执行 baseInner = new BaseInner();baseInner = new DerivedInner();

在注意到Resharper warning 并执行little more reading 之后,我决定应该更改它...但是如何更改?

我想到了几件事。我可以在调用构造函数后调用初始化器,需要调用代码来做var baseOuter = new BaseOuter(); baseOuter.Initialize();。我可能可以使用工厂,但我必须考虑一下。最后,也许我想做的类嵌套存在设计缺陷?

值得指出的是,做new BaseInner(); 的成本很高,我不想简单地创建它并扔掉它。

using System;

public class Program
{
    public static void Main()
    {
        Console.WriteLine("new BaseOuter");
        var baseOuter = new BaseOuter();
        Console.WriteLine("\nnew DerivedOuter");
        var derivedOuter = new DerivedOuter();
    }

    class BaseOuter{
        protected BaseInner baseInner;
        public BaseOuter(){
            Console.WriteLine("BaseOuter Constructor");
            /*  lots of stuff I want in derived class */

            // This is an anti-pattern I want to avoid
            //https://www.jetbrains.com/help/resharper/2018.2/VirtualMemberCallInConstructor.html
            InitializeInner();
        }

        protected virtual void InitializeInner(){
            Console.WriteLine("    BaseOuter Initialize BaseInner");
            baseInner = new BaseInner();
        }

        protected class BaseInner{
            public int x;
            public BaseInner(){
                /* stuff that is needed in DerivedInner too */
                Console.WriteLine("        BaseInner Constructor");
                x = 2;
            }
        }       
    }

    class DerivedOuter : BaseOuter {
        public DerivedOuter() {
            Console.WriteLine("DerivedOuter Constructor (finished)");
        }

        protected override void InitializeInner(){
            Console.WriteLine("    DerivedOuter Initialize DerivedInner");
            baseInner = new DerivedInner();
        }

        protected class DerivedInner : BaseInner {
            public double y;
            public DerivedInner(){
            Console.WriteLine("        DerivedInner Constructor");
                y = 2d;
            }
        }
    }
}

输出

new BaseOuter
BaseOuter Constructor
    BaseOuter Initialize BaseInner
        BaseInner Constructor

new DerivedOuter
BaseOuter Constructor
    DerivedOuter Initialize DerivedInner
        BaseInner Constructor
        DerivedInner Constructor
DerivedOuter Constructor (finished)

所有代码都可以在这个.NET Fiddle中找到。

【问题讨论】:

    标签: c# oop inheritance design-patterns


    【解决方案1】:

    这是一种方法。

    它在做同样的事情。区别在于派生类如何“覆盖”BaseInner 的实现。它通过向构造函数提供实现来实现。

    internal class BaseOuter
    {
        protected BaseInner baseInner;
    
        protected internal BaseOuter(BaseInner inner = null)
        {
            baseInner = inner ?? new BaseInner();
            Console.WriteLine("BaseOuter Constructor");
            /*  lots of stuff I want in derived class */
    
            // This is an anti-pattern I want to avoid
            //https://www.jetbrains.com/help/resharper/2018.2/VirtualMemberCallInConstructor.html
    
        }
    
        protected internal class BaseInner
        {
            public int x;
    
            public BaseInner()
            {
                /* stuff that is needed in DerivedInner too */
                Console.WriteLine("        BaseInner Constructor");
                x = 2;
            }
        }
    }
    
    internal class DerivedOuter : BaseOuter
    {
        protected internal DerivedOuter()
         :base(new DerivedInner())
        {
            Console.WriteLine("DerivedOuter Constructor (finished)");
        }
    
        protected internal class DerivedInner : BaseInner
        {
            public double y;
    
            public DerivedInner()
            {
                Console.WriteLine("        DerivedInner Constructor");
                y = 2d;
            }
        }
    }
    

    如果你想进行全面的依赖注入,你可以从

    中删除默认值
    protected internal BaseOuter(BaseInner inner = null)
    

    这意味着BaseOuter 将与BaseInner 的任何实现完全解耦。

    其他一些建议:

    • 除非有非常强烈的需要,否则我不会将类相互嵌套。它没有被大量使用,因此乍一看可能会让人感到困惑。
    • 如果应该只在调用构造函数时设置baseInner,则将其标记为readonly。 (ReSharper 可能会建议这样做。)

    【讨论】:

    • 谢谢! BaseOuter 构造函数也会有 baseInner = inner; 行,对吗?
    • 是的,我错过了一行。基类将使用派生类提供的实现(如果有的话)。但如果没有(它为空),那么它会指定它自己的。
    • 值得一提的是,如果我们需要创建许多内部实例,我们可以传入一个委托函数来执行此操作,例如(arg) => new DerivedInner(arg)
    猜你喜欢
    • 2010-09-12
    • 2014-09-13
    • 1970-01-01
    • 2012-12-19
    • 2018-07-21
    • 2016-07-19
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    相关资源
    最近更新 更多