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

如何低冗余实例化带多参数Base类的多个子类?

问题描述

现有一个Base类,其构造函数包含大量参数(无法修改这一不良设计),多个子类(如A、B、C、D)继承自它:

  • 多数子类仅复用Base的所有构造参数
  • 部分子类会额外增加1-2个专属参数

示例代码如下:

class Base {
   private readonly string _arg1;
   private readonly string _arg2;
   // ... 更多私有字段

   public Base(string arg1, string arg2, string arg3, ..., string arg_n) {
       _arg1 = arg1;
       _arg2 = arg2;
       // ... 其他字段赋值
   }
}

class A : Base {
   public A(string arg1, string arg2, string arg3, ..., string arg_n) : 
       base(arg1, arg2, arg3, ..., arg_n)
   {
   }
}

class B : Base {
   private readonly string _arg_m;

   public B(string arg1, string arg2, string arg3, ..., string arg_n, string arg_m) : 
       base(arg1, arg2, arg3, ..., arg_n)
   {
       _arg_m = arg_m;
   }
}

当前实例化子类时,需要重复传入Base的所有参数,即使这些参数完全相同:

var arg1 = "something";
// ... 初始化其他arg参数
var arg_n = "something";

var a = new A(arg1, arg2, arg3, ... , arg_n);
var b = new B(arg1, arg2, arg3, ... , arg_n, "foo");
// ... 其他子类实例化

尝试过创建Factory类封装参数,但本质只是把重复代码转移到工厂类中,仍需为每个子类编写实例化逻辑,并未真正减少冗余。希望找到低冗余的子类实例化方案。

可行的低冗余实现方案

1. 参数对象模式+泛型反射工厂

先用参数对象封装Base的所有构造参数,降低参数传递的复杂度;再结合泛型+反射动态创建子类实例,避免为每个子类编写硬编码的实例化逻辑。

步骤1:定义Base参数对象

public class BaseArgs
{
    public string Arg1 { get; set; }
    public string Arg2 { get; set; }
    // ... 对应Base构造函数的所有参数
}

步骤2:实现泛型工厂

public class GenericBaseFactory
{
    private readonly BaseArgs _baseArgs;
    private readonly Dictionary<Type, ConstructorInfo> _ctorCache = new();

    public GenericBaseFactory(BaseArgs baseArgs)
    {
        _baseArgs = baseArgs;
    }

    public T Create<T>(params object[] extraArgs) where T : Base
    {
        var type = typeof(T);
        if (!_ctorCache.TryGetValue(type, out var ctor))
        {
            // 获取Base构造函数的参数类型列表
            var baseParamTypes = typeof(Base)
                .GetConstructors()[0]
                .GetParameters()
                .Select(p => p.ParameterType)
                .ToList();
            // 拼接Base参数类型与额外参数类型
            var extraParamTypes = extraArgs.Select(a => a.GetType()).ToList();
            var allParamTypes = baseParamTypes.Concat(extraParamTypes).ToArray();
            
            // 获取子类匹配的构造函数
            ctor = type.GetConstructor(allParamTypes);
            if (ctor == null)
            {
                throw new InvalidOperationException($"找不到类型{type.Name}匹配的构造函数");
            }
            _ctorCache[type] = ctor;
        }

        // 提取Base参数对象的属性值
        var baseParamValues = typeof(BaseArgs)
            .GetProperties()
            .Select(p => p.GetValue(_baseArgs))
            .ToArray();
        // 拼接Base参数值与额外参数值
        var allParamValues = baseParamValues.Concat(extraArgs).ToArray();
        
        return (T)ctor.Invoke(allParamValues);
    }
}

使用方式

var baseArgs = new BaseArgs
{
    Arg1 = "something",
    Arg2 = "something",
    // ... 其他Base参数赋值
};

var factory = new GenericBaseFactory(baseArgs);
var a = factory.Create<A>();
var b = factory.Create<B>("foo");
// 其他子类实例化只需传入额外参数(如果有的话)

优点:无需为每个子类编写单独的工厂方法,新增子类时无需修改工厂类;参数对象让Base的参数管理更清晰。
缺点:反射有轻微性能开销,可通过构造函数缓存缓解;若子类构造函数参数顺序与Base参数+额外参数的顺序不一致会出错。

2. 泛型工厂结合表达式树(性能更优)

如果对性能要求较高,可用表达式树代替反射,提前编译构造函数的调用逻辑,避免每次实例化的反射开销。

实现示例

public class ExpressionBaseFactory
{
    private readonly BaseArgs _baseArgs;
    private readonly Dictionary<Type, Delegate> _factoryCache = new();

    public ExpressionBaseFactory(BaseArgs baseArgs)
    {
        _baseArgs = baseArgs;
    }

    public T Create<T>(params object[] extraArgs) where T : Base
    {
        var type = typeof(T);
        if (!_factoryCache.TryGetValue(type, out var factory))
        {
            // 创建额外参数的表达式参数
            var extraParams = extraArgs
                .Select((_, index) => Expression.Parameter(typeof(object), $"extra{index}"))
                .ToArray();
            
            // 获取Base参数值的常量表达式
            var baseParamValues = typeof(BaseArgs)
                .GetProperties()
                .Select(p => Expression.Constant(p.GetValue(_baseArgs), p.PropertyType))
                .ToArray();
            
            // 获取子类匹配的构造函数
            var ctor = GetMatchingConstructor(type, 
                baseParamValues.Select(p => p.Type).Concat(extraArgs.Select(a => a.GetType())).ToArray());
            
            // 转换额外参数到构造函数要求的类型
            var convertedExtraParams = ctor.GetParameters()
                .Skip(baseParamValues.Length)
                .Select((param, index) => Expression.Convert(extraParams[index], param.ParameterType))
                .ToArray();
            
            // 拼接所有构造参数并创建实例表达式
            var allParams = baseParamValues.Concat(convertedExtraParams).ToArray();
            var newExpr = Expression.New(ctor, allParams);
            
            // 编译成可直接调用的委托
            factory = Expression.Lambda<Func<object[], T>>(newExpr, extraParams).Compile();
            _factoryCache[type] = factory;
        }

        return ((Func<object[], T>)factory)(extraArgs);
    }

    private ConstructorInfo GetMatchingConstructor(Type type, Type[] paramTypes)
    {
        var ctor = type.GetConstructor(paramTypes);
        if (ctor == null)
        {
            throw new InvalidOperationException($"找不到类型{type.Name}匹配的构造函数");
        }
        return ctor;
    }
}

使用方式和泛型反射工厂一致,但性能更接近硬编码的实例化逻辑。

3. 简化方案:为子类添加静态创建方法(适合子类数量不多的场景)

如果子类数量较少,可在每个子类中添加静态创建方法,复用Base的参数对象:

class A : Base
{
    public A(string arg1, string arg2, ..., string arg_n) : base(arg1, arg2, ..., arg_n) { }

    public static A Create(BaseArgs baseArgs)
    {
        return new A(baseArgs.Arg1, baseArgs.Arg2, ..., baseArgs.Arg_n);
    }
}

class B : Base
{
    private readonly string _arg_m;
    public B(string arg1, string arg2, ..., string arg_n, string arg_m) : base(arg1, arg2, ..., arg_n)
    {
        _arg_m = arg_m;
    }

    public static B Create(BaseArgs baseArgs, string arg_m)
    {
        return new B(baseArgs.Arg1, baseArgs.Arg2, ..., baseArgs.Arg_n, arg_m);
    }
}

使用方式

var baseArgs = new BaseArgs { /* 参数赋值 */ };
var a = A.Create(baseArgs);
var b = B.Create(baseArgs, "foo");

优点:性能最优,代码直观;缺点:新增子类时需要编写对应的静态方法,仍有少量重复,但比硬编码实例化简洁很多。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 05:17:05