如何在保留初始化所需子对象的同时初始化基类?
问题描述
我长期以来一直遇到这个问题,始终找不到理想的解决方案。我清楚基类构造函数必须先于子类构造函数执行,但如果我需要用一个对象初始化基类,而该对象本身依赖一个子类需要但基类不应知晓的子对象时,构造函数的调用机制似乎让这一需求难以实现。
示例代码:
sealed class NeededByBase { readonly NeededByChild mNeededByChild; public NeededByBase(NeededByChild neededByChild) => mNeededByChild = neededByChild; } sealed class NeededByChild { } class BaseClass { readonly NeededByBase mNeededByBase; protected BaseClass(NeededByBase neededByBase) => mNeededByBase = neededByBase; } sealed class ChildClass : BaseClass { readonly NeededByChild mNeededByChild; // 这段代码无法运行(静态上下文限制) public ChildClass() : base( new NeededByBase(mNeededByChild = new NeededByChild()) ) { } }
请问是否存在一种方法,能在不让基类知晓子类所需子对象的前提下实现这一需求?我在使用虚/抽象成员或委托时也遇到了类似问题。我本可以为ChildClass添加一个接收NeededByBase的构造函数,但场景要求ChildClass必须具备默认构造函数。
解决方案
这里有几种可行的实现方式,都能满足你的需求:
1. 静态工厂方法传递实例
通过静态方法的out参数,在基类构造函数执行前创建NeededByChild实例,同时把引用传递给子类成员:
sealed class ChildClass : BaseClass { readonly NeededByChild mNeededByChild; public ChildClass() : base(new NeededByBase(CreateNeededByChild(out var neededByChild))) { mNeededByChild = neededByChild; } private static NeededByChild CreateNeededByChild(out NeededByChild instance) { instance = new NeededByChild(); return instance; } }
这种方式完全符合C#构造函数的执行规则,基类全程不知道NeededByChild的存在,代码简洁且没有封装性问题。
2. 重构依赖(最优方案)
如果允许调整类结构,最好通过抽象接口解耦NeededByBase和NeededByChild的依赖:
// 定义抽象接口,作为两者的依赖契约 interface INeededComponent { } sealed class NeededByChild : INeededComponent { } sealed class NeededByBase { readonly INeededComponent mComponent; public NeededByBase(INeededComponent component) => mComponent = component; } class BaseClass { readonly NeededByBase mNeededByBase; protected BaseClass(NeededByBase neededByBase) => mNeededByBase = neededByBase; } sealed class ChildClass : BaseClass { readonly NeededByChild mNeededByChild; public ChildClass() : base(new NeededByBase(mNeededByChild = new NeededByChild())) { } }
这种方式遵循面向对象的依赖倒置原则,基类只依赖抽象接口,完全不知道具体的NeededByChild类型,同时子类可以正常持有自己需要的实例,是最合理的长期解决方案。
3. 惰性初始化(备选方案)
如果无法修改NeededByBase的代码,且它对NeededByChild的访问不是构造阶段必须的,可以用惰性初始化延迟实例创建:
sealed class NeededByBase { readonly Lazy<NeededByChild> mNeededByChild; public NeededByBase(Lazy<NeededByChild> neededByChild) => mNeededByChild = neededByChild; // 需要使用时再获取实例 public NeededByChild GetChildComponent() => mNeededByChild.Value; } sealed class ChildClass : BaseClass { readonly NeededByChild mNeededByChild; public ChildClass() : base(new NeededByBase(new Lazy<NeededByChild>(() => new NeededByChild()))) { // 从基类的NeededByBase中取出实例 mNeededByChild = ((Lazy<NeededByChild>)typeof(NeededByBase) .GetField("mNeededByChild", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) .GetValue(mNeededByBase)).Value; } }
注意:这种方式用到了反射,会破坏类的封装性,仅作为无法修改依赖类时的临时方案。
内容的提问来源于stack exchange,提问作者Dave Cousineau
相关产品推荐
相关产品推荐

