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

使用NSubstitute创建实例时如何避免调用目标类的基类构造函数

核心原因

.NET 运行时的类型实例化规则要求,任何子类实例创建时都会沿继承链依次执行父类构造函数,这个逻辑无法通过 NSubstitute 的配置直接跳过。你提到的 DoNotCallBase 仅作用于普通类方法的调用逻辑,对构造函数无效。


方案1:配置构造依赖让基类构造正常执行(优先推荐)

如果可以明确 FooBase 构造函数中对 IBar 参数的依赖逻辑,直接提前配置好 IBar 替代实例的对应行为,让基类构造函数可以无异常执行完,即可正常创建部分替代实例:

var mockBar = Substitute.For<IBar>();
// 提前配置 FooBase 构造函数会调用的 IBar 成员的返回值/行为
mockBar.构造函数调用的方法名().Returns(预期返回值);
// 正常创建部分替代实例
var partialFoo = Substitute.ForPartsOf<Foo>(mockBar);

// 后续正常配置其他方法是否调用基类即可
partialFoo.When(x => x.需要跳过基类的方法()).DoNotCallBase();

该方案成本最低,无需额外调整代码结构。


方案2:用适配器模式解耦对不可控类型的直接依赖

如果无法满足基类构造的执行条件,建议通过适配器模式隔离不可控的 Foo 类型,完全绕过基类构造的问题:

  1. 定义适配接口,声明你需要用到的 Foo 的所有公开方法
  2. 实现适配器类,内部持有真实 Foo 实例,转发方法调用
  3. 测试时直接 Mock 适配接口即可
// 1. 定义适配接口
public interface IFoo
{
    void 你用到的Foo方法1();
    int 你用到的Foo方法2(string param);
}

// 2. 实现适配器
public class FooAdapter : IFoo
{
    private readonly Foo _realFoo;
    public FooAdapter(Foo realFoo) => _realFoo = realFoo;

    public void 你用到的Foo方法1() => _realFoo.你用到的Foo方法1();
    public int 你用到的Foo方法2(string param) => _realFoo.你用到的Foo方法2(param);
}

// 3. 测试时直接Mock接口,完全不需要实例化Foo
var mockFoo = Substitute.For<IFoo>();
mockFoo.你用到的Foo方法2("test").Returns(100);

该方案符合面向接口的设计原则,后续不受 Foo 或 FooBase 内部变更的影响,维护成本最低。


方案3:用反射跳过构造函数实例化(不推荐,仅当前两种方案不可行时使用)

如果一定要直接操作 Foo 实例且无法执行基类构造,可以使用 .NET 序列化相关的 FormatterServices 类,不调用任何构造函数创建对象实例:

using System.Runtime.Serialization;
using System.Reflection;

// 不调用任何构造函数创建Foo实例
var uninitializedFoo = (Foo)FormatterServices.GetUninitializedObject(typeof(Foo));

// 手动给Foo/FooBase的必要字段赋值(根据实际需要调整)
var barField = typeof(FooBase).GetField("_bar", BindingFlags.Instance | BindingFlags.NonPublic);
barField?.SetValue(uninitializedFoo, Substitute.For<IBar>());

⚠️ 注意:该方式创建的对象所有字段都是默认值,若后续调用的方法依赖构造函数初始化的状态,会出现不可预期的运行错误,调试成本极高。


内容的提问来源于stack exchange,提问作者lonix

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 17:36:00