You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在保留初始化所需子对象的同时初始化基类?

问题描述

我长期以来一直遇到这个问题,始终找不到理想的解决方案。我清楚基类构造函数必须先于子类构造函数执行,但如果我需要用一个对象初始化基类,而该对象本身依赖一个子类需要但基类不应知晓的子对象时,构造函数的调用机制似乎让这一需求难以实现。

示例代码:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 20:38:26