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

C#中不使用ICloneable接口如何克隆外部库的引用类型对象?

克隆外部库对象的可行方案(无需逐个赋值或修改原类)

由于你无法修改外部库中的Point类,也不想手动逐个复制属性,以下几种方法可以实现对象克隆,确保修改副本时不影响原对象:

1. 序列化反序列化法

通过将对象序列化为JSON再反序列化,生成独立的对象副本。这种方法无需关注对象的具体属性,实现简单。

示例代码(使用System.Text.Json):

using System.Text.Json;

public static class CloneExtensions
{
    public static T DeepClone<T>(this T source) where T : class
    {
        var jsonString = JsonSerializer.Serialize(source);
        return JsonSerializer.Deserialize<T>(jsonString);
    }
}

// 使用方式
Point p1 = new Point(5, 6);
Point p2 = p1.DeepClone();
p2.X = 9;
// p1的X值仍为5,不受修改p2的影响

注意事项:

  • 此方法适用于大多数公共属性可访问的对象,无需原类标记[Serializable](System.Text.Json默认支持)。
  • 性能略低于反射,但实现成本极低,适合非高频克隆场景。

2. 反射自动复制属性

利用反射遍历对象的公共可读写属性,自动将原对象的属性值复制到新实例中,避免手动逐个赋值。

基础版本代码:

using System.Reflection;

public static class CloneExtensions
{
    public static T ShallowClone<T>(this T source) where T : class, new()
    {
        var clone = new T();
        foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (prop.CanRead && prop.CanWrite)
            {
                prop.SetValue(clone, prop.GetValue(source));
            }
        }
        return clone;
    }
}

// 使用方式
Point p1 = new Point(5, 6);
Point p2 = p1.ShallowClone();
p2.X = 9;
// p1保持原始状态

性能优化版本(缓存反射信息):

如果需要频繁克隆对象,可以缓存反射获取的属性信息,避免重复反射开销:

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

public static class CloneExtensions
{
    private static readonly Dictionary<Type, PropertyInfo[]> _propertyCache = new Dictionary<Type, PropertyInfo[]>();

    public static T ShallowClone<T>(this T source) where T : class, new()
    {
        Type type = typeof(T);
        if (!_propertyCache.TryGetValue(type, out PropertyInfo[] props))
        {
            props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                        .Where(p => p.CanRead && p.CanWrite)
                        .ToArray();
            _propertyCache[type] = props;
        }

        var clone = new T();
        foreach (var prop in props)
        {
            prop.SetValue(clone, prop.GetValue(source));
        }
        return clone;
    }
}

3. 表达式树编译(高性能替代反射)

通过表达式树编译成委托,性能接近手动赋值,适合需要高频克隆的场景。

示例代码:

using System.Linq.Expressions;
using System.Collections.Generic;

public static class CloneExtensions
{
    private static readonly Dictionary<Type, Delegate> _cloneDelegateCache = new Dictionary<Type, Delegate>();

    public static T ShallowClone<T>(this T source) where T : class, new()
    {
        Type type = typeof(T);
        if (!_cloneDelegateCache.TryGetValue(type, out Delegate cloneDelegate))
        {
            ParameterExpression sourceParam = Expression.Parameter(type, "source");
            NewExpression newInstance = Expression.New(type);

            List<MemberBinding> propertyBindings = new List<MemberBinding>();
            foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                                     .Where(p => p.CanRead && p.CanWrite))
            {
                MemberExpression sourcePropExpr = Expression.Property(sourceParam, prop);
                propertyBindings.Add(Expression.Bind(prop, sourcePropExpr));
            }

            MemberInitExpression initExpr = Expression.MemberInit(newInstance, propertyBindings);
            cloneDelegate = Expression.Lambda<Func<T, T>>(initExpr, sourceParam).Compile();
            _cloneDelegateCache[type] = cloneDelegate;
        }

        return ((Func<T, T>)cloneDelegate)(source);
    }
}

// 使用方式
Point p1 = new Point(5, 6);
Point p2 = p1.ShallowClone();
p2.X = 9;
// p1的X值不会被修改

针对你的测试代码的效果

原代码中Point p2 = p1;是引用赋值,修改p2.X会直接影响p1。使用上述任意一种克隆方法后,p2是独立的实例,修改其属性不会改变p1的状态,完全符合你的需求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 00:05:34