如何低冗余实例化带多参数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
相关产品推荐
相关产品推荐

