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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:55:22