如何通用递归检查反序列化对象的非空字段?求工具或实现思路
通用非空字段验证方案
已有工具推荐
- FluentValidation:一款常用的NuGet验证库,支持通过反射或约定实现通用验证逻辑。你可以创建通用验证器,自动检查所有标记为非空的字段(比如基于
[Required]特性,或根据类型判断值类型/引用类型的非空要求)。 - System.ComponentModel.DataAnnotations:.NET自带的验证库,结合
Validator.TryValidateObject方法,可批量验证对象上[Required]等特性标记的字段,但要求对象本身带有对应的特性标注。
自行实现思路
如果需要完全自定义的通用验证逻辑,可基于反射实现,核心步骤如下:
- 遍历对象成员:通过反射获取目标类型
T的所有实例属性或字段(根据实际需求选择)。 - 判断非空规则:
- 引用类型(除显式标记的可空引用类型)默认视为非空;
- 值类型中,可空值类型(如
int?)允许为空,非可空值类型本身不会为null; - 也可自定义
[NonNull]特性,明确标记需要验证的非空字段。
- 检查空值:若字段应为非空但实际值为null,收集错误信息。
- 递归验证嵌套对象:如果字段是自定义复杂类型,递归调用验证方法确保嵌套对象的非空字段也被检查。
简易实现示例
public static class FieldAsserter { public static void AssertAllFieldsSet<T>(T obj) { if (obj == null) throw new ArgumentNullException(nameof(obj)); var type = typeof(T); var errors = new List<string>(); // 遍历所有公开实例属性(如需验证字段可改用GetFields) foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) { // 跳过索引器属性 if (prop.GetIndexParameters().Length > 0) continue; var value = prop.GetValue(obj); var isNullable = IsNullableType(prop.PropertyType); if (!isNullable && value == null) { errors.Add($"属性 '{prop.Name}'(类型 '{type.Name}')为null,但应为非空字段。"); } // 递归验证嵌套对象(值不为null且为非基础引用类型时) else if (value != null && !prop.PropertyType.IsValueType && !prop.PropertyType.IsPrimitive && prop.PropertyType != typeof(string)) { try { // 调用泛型验证方法 typeof(FieldAsserter).GetMethod(nameof(AssertAllFieldsSet))! .MakeGenericMethod(prop.PropertyType) .Invoke(null, new[] { value }); } catch (TargetInvocationException ex) { // 捕获递归中的异常并合并错误信息 errors.AddRange(ex.InnerException!.Message.Split(Environment.NewLine)); } } } if (errors.Any()) throw new InvalidOperationException(string.Join(Environment.NewLine, errors)); } private static bool IsNullableType(Type type) { // 处理可空值类型 if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) return true; // 处理C# 8+的可空引用类型(需启用nullable上下文) if (!type.IsValueType) { var nullableAttr = type.CustomAttributes.FirstOrDefault(attr => attr.AttributeType == typeof(NullableAttribute)); return nullableAttr != null && ((byte[])nullableAttr.ConstructorArguments[0].Value!)[0] == 2; } return false; } }
注意事项
- 可空引用类型:若项目启用了nullable上下文,需确保
IsNullableType方法能正确识别可空引用类型的标记; - 性能优化:反射操作存在一定性能开销,若处理大量消息,建议缓存反射获取的类型元数据;
- 特性扩展:可修改逻辑,仅验证带有特定特性(如
[Required])的字段,提升灵活性。
内容的提问来源于stack exchange,提问作者SRNissen
相关产品推荐
相关产品推荐

