基于规则的验证层实现疑问:泛型规则集合存储与Validator实现
实现基于规则的验证层:解决方案与思路修正
首先,你的核心思路是对的——构建一个可以聚合多种验证规则的Validator,但当前的实现有两个关键问题需要解决:
- 无法将不同泛型的
IValidationRule<T>存入同一个集合(因为每个IValidationRule<string>、IValidationRule<int>都是不同的具体类型) - 使用
dynamic arg会丢失编译时类型安全,容易引入运行时错误
修正方案:引入非泛型统一接口
要解决泛型规则的统一存储问题,我们需要一个非泛型的基接口来作为所有验证规则条目的通用类型,然后通过泛型类封装具体的规则和待验证参数。
步骤1:定义非泛型的验证条目接口
这个接口提供统一的验证执行和错误获取方法,不需要关心具体的泛型类型:
public interface IValidationEntry { string Error { get; } bool ExecuteValidation(); }
步骤2:实现泛型验证条目类
这个类负责绑定具体的IValidationRule<T>和待验证的参数T,并实现非泛型接口:
public class ValidationRuleEntry<T> : IValidationEntry { private readonly IValidationRule<T> _rule; private readonly T _validationArg; public ValidationRuleEntry(IValidationRule<T> rule, T validationArg) { _rule = rule; _validationArg = validationArg; } public string Error => _rule.Error; public bool ExecuteValidation() { return _rule.Validate(_validationArg); } }
步骤3:重构Validator类
现在Validator可以维护一个List<IValidationEntry>集合,同时提供类型安全的AddRule方法:
public interface IValidator { // 重载1:传入已实例化的规则和参数(推荐,类型安全) void AddRule<T>(IValidationRule<T> rule, T arg); // 重载2:通过规则类型自动实例化(适合无状态规则) void AddRule<TRule, T>(T arg) where TRule : IValidationRule<T>, new(); ValidationResult Validate(); } public class Validator : IValidator { private readonly List<IValidationEntry> _validationEntries = new(); public void AddRule<T>(IValidationRule<T> rule, T arg) { _validationEntries.Add(new ValidationRuleEntry<T>(rule, arg)); } public void AddRule<TRule, T>(T arg) where TRule : IValidationRule<T>, new() { var ruleInstance = new TRule(); _validationEntries.Add(new ValidationRuleEntry<T>(ruleInstance, arg)); } public ValidationResult Validate() { var errors = new List<string>(); bool isValid = true; foreach (var entry in _validationEntries) { if (!entry.ExecuteValidation()) { isValid = false; if (!string.IsNullOrWhiteSpace(entry.Error)) { errors.Add(entry.Error); } } } return new ValidationResult(isValid, errors); } } // 假设你的ValidationResult定义如下(可根据需求调整) public class ValidationResult { public bool IsValid { get; } public IReadOnlyList<string> Errors { get; } public ValidationResult(bool isValid, IEnumerable<string> errors) { IsValid = isValid; Errors = errors.ToList().AsReadOnly(); } }
步骤4:调用端代码调整
根据你的需求,调用端可以选择两种方式添加规则:
var validator = new Validator(); // 方式1:传入已实例化的规则(支持自定义规则初始化逻辑) var testRule = new TestRule { Error = "字符串不是'test'" }; validator.AddRule(testRule, "OK"); // 方式2:通过规则类型自动实例化(适合无状态规则) validator.AddRule<IntRangeRule, int>(45); // 假设你有IntRangeRule实现IValidationRule<int> var result = validator.Validate(); if (!result.IsValid) { foreach (var error in result.Errors) { Console.WriteLine(error); } }
关于你原调用方式的兼容(可选)
如果你坚持要使用AddRule<TestRule>("OK")这种只指定规则类型的写法,可以通过反射实现,但会丢失编译时类型安全:
public void AddRule<TRule>(dynamic arg) { var ruleType = typeof(TRule); // 找到TRule实现的IValidationRule<T>接口 var genericRuleInterface = ruleType.GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IValidationRule<>)); if (genericRuleInterface == null) { throw new InvalidOperationException($"{ruleType.Name} 未实现IValidationRule<T>接口"); } var targetType = genericRuleInterface.GetGenericArguments()[0]; if (arg.GetType() != targetType) { throw new ArgumentException($"参数类型与规则期望的{targetType.Name}不匹配"); } var ruleInstance = Activator.CreateInstance<TRule>(); var entryType = typeof(ValidationRuleEntry<>).MakeGenericType(targetType); var entry = Activator.CreateInstance(entryType, ruleInstance, arg); _validationEntries.Add((IValidationEntry)entry); }
但这种方式不推荐,因为编译时无法检查参数类型是否匹配,容易引入运行时错误。
总结
你的核心思路是可行的,但需要通过非泛型接口统一存储来解决泛型规则的类型差异问题,同时尽量使用类型安全的泛型方法替代dynamic,避免不必要的运行时风险。
内容的提问来源于stack exchange,提问作者Max Bertoli
相关产品推荐
相关产品推荐

