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

C#遍历Shipment对象及嵌套属性,实现空值填充需求

解决反射处理嵌套对象与集合的属性填充问题

我完全理解你遇到的痛点——反射处理简单属性还好,但碰到嵌套对象(比如Consignor、Consignee)和集合(比如List<Invoice>)时,逻辑会复杂很多。下面我会给你一套完整的解决方案,一步步处理不同类型的属性:

1. 核心思路

我们需要编写一个通用的反射工具方法,它能:

  • 遍历对象的所有公共属性
  • 对简单值类型/字符串:判断源对象(Shipment1)属性是否为null或空,是则用目标对象(Shipment2)的对应值覆盖
  • 对嵌套引用对象(比如Consignor):递归调用填充方法,因为这些对象本身也有需要检查的属性
  • 对集合类型(比如List):需要先判断源集合是否为null或空,再处理集合中的每个元素(或者直接替换集合,根据你的需求)

2. 完整代码实现

首先假设你的嵌套类结构大概是这样(你可以根据实际情况调整):

public class Consignor 
{
    public string Name { get; set; }
    public string Address { get; set; }
    // 其他属性...
}

public class Consignee 
{
    public string Name { get; set; }
    public string Contact { get; set; }
    // 其他属性...
}

public class Invoice 
{
    public int InvoiceId { get; set; }
    public decimal Amount { get; set; }
    // 其他属性...
}

然后编写通用的填充方法:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;

public static class ObjectMergeHelper
{
    public static void MergeNullProperties<T>(T source, T target) where T : class
    {
        if (source == null || target == null)
            throw new ArgumentNullException("Source or target object cannot be null");

        Type type = typeof(T);
        foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            // 获取源对象的属性值
            object sourceValue = property.GetValue(source);
            // 获取目标对象的属性值
            object targetValue = property.GetValue(target);

            // 跳过只读属性
            if (!property.CanWrite)
                continue;

            // 处理不同类型的属性
            if (IsSimpleType(property.PropertyType))
            {
                // 简单类型:检查是否为null或空值(字符串单独判断空)
                bool isSourceEmpty = IsNullOrEmptyValue(sourceValue);
                if (isSourceEmpty && targetValue != null)
                {
                    property.SetValue(source, targetValue);
                }
            }
            else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && property.PropertyType != typeof(string))
            {
                // 处理集合类型(排除字符串)
                MergeCollections(sourceValue, targetValue, property, source);
            }
            else if (property.PropertyType.IsClass)
            {
                // 嵌套引用对象:递归调用合并方法
                if (sourceValue == null && targetValue != null)
                {
                    // 如果源对象的嵌套属性是null,直接创建一个新实例并复制目标值
                    object newInstance = Activator.CreateInstance(property.PropertyType);
                    MergeNullProperties(newInstance, targetValue);
                    property.SetValue(source, newInstance);
                }
                else if (sourceValue != null && targetValue != null)
                {
                    // 如果源对象的嵌套属性已存在,递归合并其内部属性
                    MergeNullProperties(sourceValue, targetValue);
                }
            }
        }
    }

    // 判断是否为简单类型(值类型+字符串)
    private static bool IsSimpleType(Type type)
    {
        return type.IsValueType || type == typeof(string);
    }

    // 判断值是否为null或空(字符串判断Empty)
    private static bool IsNullOrEmptyValue(object value)
    {
        if (value == null)
            return true;
        if (value is string str)
            return string.IsNullOrEmpty(str);
        return false;
    }

    // 处理集合的合并逻辑
    private static void MergeCollections(object sourceCollection, object targetCollection, PropertyInfo property, object sourceObj)
    {
        // 如果源集合是null或空,且目标集合有内容,直接替换源集合
        if ((sourceCollection == null || !((IEnumerable)sourceCollection).GetEnumerator().MoveNext()) && targetCollection != null)
        {
            // 这里可以选择直接复制集合,或者逐个元素合并,根据你的需求
            // 示例:直接创建一个新的同类型集合并复制元素
            Type genericType = property.PropertyType.GetGenericArguments()[0];
            Type listType = typeof(List<>).MakeGenericType(genericType);
            IList newCollection = (IList)Activator.CreateInstance(listType);
            
            foreach (var item in (IEnumerable)targetCollection)
            {
                // 如果是引用类型,需要创建副本避免引用共享;值类型直接添加
                if (genericType.IsClass && genericType != typeof(string))
                {
                    object itemCopy = Activator.CreateInstance(genericType);
                    MergeNullProperties(itemCopy, item);
                    newCollection.Add(itemCopy);
                }
                else
                {
                    newCollection.Add(item);
                }
            }
            
            property.SetValue(sourceObj, newCollection);
        }
        else if (sourceCollection != null && targetCollection != null)
        {
            // 如果源集合已有内容,你可以选择合并元素(比如根据ID匹配),或者跳过
            // 这里示例是跳过,如果你需要更复杂的集合合并,可以扩展逻辑
            // 比如:遍历目标集合,将源集合中不存在的元素添加进去,或者更新匹配的元素
        }
    }
}

3. 使用方法

在你的业务代码中,直接调用这个工具方法即可:

// 假设你已经有了Shipment1和Shipment2实例
Shipment shipment1 = new Shipment();
// shipment1的部分属性为null或空...

Shipment shipment2 = new Shipment();
// shipment2有完整的属性值...

// 执行合并:将shipment2的非空属性填充到shipment1的空属性中
ObjectMergeHelper.MergeNullProperties(shipment1, shipment2);

4. 关键细节说明

  • 递归处理嵌套对象:对于Consignor、Consignee这类嵌套类,我们递归调用MergeNullProperties,确保它们的内部属性也会被检查和填充
  • 集合处理逻辑:示例中是当源集合为空时,直接复制目标集合的元素(并创建副本避免引用共享),你可以根据实际需求调整——比如如果需要合并集合中已有的元素(比如根据InvoiceId匹配更新),可以修改MergeCollections方法
  • 简单类型判断:区分简单类型和复杂类型,避免对嵌套对象做简单的null判断
  • 空值判断:单独处理字符串的NullOrEmpty,其他值类型只判断null(不过值类型本身不能为null,除非是可空值类型,代码中也能处理)

这个方案应该能解决你遇到的嵌套对象和集合的反射处理问题,你可以根据自己的类结构和业务需求再调整细节~

内容的提问来源于stack exchange,提问作者RimKaMz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:11:10