C#:如何获取类类型成员的属性?递归填充类缺失值疑问
解决嵌套对象的递归填充问题
我来帮你搞定这个递归填充嵌套对象的问题——这确实是对象属性填充场景里很常见的痛点,尤其是处理多层嵌套的实体类时。下面是改进后的完整实现,能自动处理嵌套类的内部属性填充:
1. 先定义示例实体类
假设你的类结构是这样的:
public class UserLogin { public string? Username { get; set; } public string? PasswordHash { get; set; } } public class User { public int? Id { get; set; } public string? Name { get; set; } public UserLogin? LoginInfo { get; set; } }
2. 改进后的递归填充扩展方法
核心逻辑是:遍历属性时,判断属性类型是否为需要递归处理的引用类型(排除string这种特殊的不可变引用类型),然后自动递归调用填充方法处理嵌套对象的内部属性:
public static class ObjectExtensions { public static void FillValues<T>(this T target, T source) where T : class { // 空值防护,避免空引用异常 if (target == null || source == null) return; // 获取当前类的所有公共实例属性 var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (var prop in properties) { // 跳过只读属性,防止赋值失败 if (!prop.CanWrite) continue; var targetValue = prop.GetValue(target); var sourceValue = prop.GetValue(source); // 如果目标值是null或值类型的默认值,直接用源值填充 if (IsDefaultOrNull(targetValue)) { prop.SetValue(target, sourceValue); } else { // 处理嵌套的自定义引用类型(排除string) var propType = prop.PropertyType; if (propType.IsClass && propType != typeof(string) && sourceValue != null) { // 通过反射获取泛型填充方法,递归调用处理嵌套对象 var fillMethod = typeof(ObjectExtensions) .GetMethod(nameof(FillValues))? .MakeGenericMethod(propType); fillMethod?.Invoke(null, new[] { targetValue, sourceValue }); } } } } // 辅助方法:判断值是否为null或值类型的默认值 private static bool IsDefaultOrNull(object? value) { if (value == null) return true; var valueType = value.GetType(); // 对值类型,检查是否等于该类型的默认值(比如int的0、bool的false) return valueType.IsValueType && Equals(value, Activator.CreateInstance(valueType)); } }
3. 用法演示
我们来测试一下这个方法的实际效果:
// 源对象:包含完整的用户数据 var sourceUser = new User { Id = 1, Name = "Liran Friedman", LoginInfo = new UserLogin { Username = "liran_f", PasswordHash = "hashed_pass123" } }; // 目标对象:部分属性缺失,部分已有值 var targetUser = new User { Name = "Liran", // 已有值,不会被覆盖 LoginInfo = new UserLogin { PasswordHash = "old_hash" // 已有值,不会被覆盖 } }; // 调用填充方法 targetUser.FillValues(sourceUser); // 最终结果: // targetUser.Id = 1(从源填充) // targetUser.Name = "Liran"(保留原有值) // targetUser.LoginInfo.Username = "liran_f"(从源填充) // targetUser.LoginInfo.PasswordHash = "old_hash"(保留原有值)
关键细节说明
- 空值安全:开头的空值判断避免了传入null时的异常
- 只读属性跳过:只处理有setter的属性,防止赋值失败
- 递归逻辑:对于嵌套的自定义类(比如
UserLogin),只要目标对象的该属性不为null,就递归填充其内部属性;如果目标的该属性是null,直接用源对象的属性值赋值 - 值类型处理:
IsDefaultOrNull方法会判断值类型的默认值,确保只有缺失的默认值才会被源值覆盖
如果需要处理集合类型(比如List<T>),可以在方法里额外判断propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(List<>),然后遍历集合元素进行递归填充——这个可以根据你的具体需求扩展。
内容的提问来源于stack exchange,提问作者Liran Friedman
相关产品推荐
相关产品推荐

