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

.NET如何实现通用的‘复制到引用’对象扩展方法?

我完全懂你的需求——不想创建新对象替换List里的元素,而是直接把源对象的所有属性值,批量复制到List中已存在的那个对象引用里,省得手动一个个属性赋值对吧?这确实可以实现通用的扩展方法,下面给你两种实用方案:

方案一:反射实现(简单易上手)

反射是最直接的通用实现方式,它可以动态获取对象的属性信息,然后批量赋值。我们可以写一个扩展方法,适用于所有类:

public static class ObjectExtensions
{
    // 扩展方法:把源对象的属性值复制到目标对象引用中
    public static void CopyPropertiesTo<T>(this T source, T target)
    {
        // 空值校验
        if (source == null)
            throw new ArgumentNullException(nameof(source));
        if (target == null)
            throw new ArgumentNullException(nameof(target));

        // 获取类型的公共实例属性:只取可读可写、非索引器的属性
        var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                  .Where(p => p.CanRead && p.CanWrite && !p.GetIndexParameters().Any());

        // 逐个复制属性值
        foreach (var prop in properties)
        {
            var value = prop.GetValue(source);
            prop.SetValue(target, value);
        }
    }
}

使用示例

假设你有一个Person类,List里已经有一些Person对象:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

// 场景:从List中找到目标对象,把源数据复制进去
var sourcePerson = new Person { Name = "Bob", Age = 28 };
var targetPerson = myPersonList.First(p => p.Id == 101); // 拿到List里已存在的对象引用

// 调用扩展方法
sourcePerson.CopyPropertiesTo(targetPerson);

// 现在targetPerson的Name和Age已经更新,而且它还是List里的那个对象,List不需要做任何替换操作
方案二:表达式树实现(性能更优)

反射每次调用都会遍历属性,性能略差。如果你的代码需要高频调用这个复制方法,可以用表达式树——它会在第一次调用时编译好赋值逻辑,后续直接执行编译后的委托,性能和手动赋值差不多:

public static class ObjectExtensions
{
    // 缓存编译好的复制委托,避免重复编译
    private static readonly Dictionary<Type, Action<object, object>> _copyActionCache = new Dictionary<Type, Action<object, object>>();

    public static void CopyPropertiesTo<T>(this T source, T target)
    {
        if (source == null)
            throw new ArgumentNullException(nameof(source));
        if (target == null)
            throw new ArgumentNullException(nameof(target));

        var type = typeof(T);
        // 先从缓存取,没有的话就编译新的委托
        if (!_copyActionCache.TryGetValue(type, out var copyAction))
        {
            copyAction = CreateCopyAction<T>();
            _copyActionCache[type] = copyAction;
        }

        // 执行复制逻辑
        copyAction(source, target);
    }

    // 动态创建赋值的表达式树并编译
    private static Action<object, object> CreateCopyAction<T>()
    {
        var sourceParam = Expression.Parameter(typeof(object), "source");
        var targetParam = Expression.Parameter(typeof(object), "target");

        // 把object类型的参数强转成T类型
        var sourceCast = Expression.Convert(sourceParam, typeof(T));
        var targetCast = Expression.Convert(targetParam, typeof(T));

        // 生成所有属性的赋值表达式
        var assignments = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                   .Where(p => p.CanRead && p.CanWrite && !p.GetIndexParameters().Any())
                                   .Select(p => Expression.Assign(
                                       Expression.Property(targetCast, p),
                                       Expression.Property(sourceCast, p)
                                   ));

        // 把所有赋值表达式打包成一个代码块
        var body = Expression.Block(assignments);
        // 编译成委托
        var lambda = Expression.Lambda<Action<object, object>>(body, sourceParam, targetParam);

        return lambda.Compile();
    }
}

额外说明

  • 这两种方法都是浅拷贝:如果属性是引用类型(比如另一个类、List等),复制的是对象引用,而不是创建新的子对象。如果需要深拷贝,你可以在属性遍历的时候额外处理引用类型的属性(比如递归调用这个方法)。
  • 如果有个别属性不想被复制,可以自定义一个特性(比如[SkipCopy]),然后在过滤属性的时候排除带有这个特性的属性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:47:52