首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 淺拷貝與深拷貝完整解析:MemberwiseClone、ICloneable、序列化、反射四大實作模式

     編輯:關於C#

物件拷貝是 C# 開發常見需求,但「賦值 (=)」、「淺拷貝」、「深拷貝」三者行為差異巨大,搞混會導致資料共享異常。本文系統整理四種主流深拷貝實作模式,並釐清常見誤區。

核心概念釐清

方式值型別欄位參考型別欄位適用場景
賦值 (=)複製值複製**參考** (雙方指向同一物件)刻意共享參考
淺拷貝逐位複製複製**參考** (雙方指向同一子物件)簡單 DTO、不可變子物件
深拷貝逐位複製**遞迴建立新子物件** (完全獨立)複雜物件圖、需隔離修改

特例:string 雖為參考型別,但不可變,淺拷貝行為等同深拷貝。

模式一:MemberwiseClone (淺拷貝標準實作)

public class Person
{
    public int Age { get; set; }
    public string Address { get; set; } // string 不可變,行同深拷貝
    public Name Name { get; set; }      // 參考型別,淺拷貝共享此實例

    public object ShallowClone()
    {
        return this.MemberwiseClone(); // protected 方法,需公開包裝
    }
}

public class Name
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

優點:效能極高 (CLR 內建)、程式碼極少。缺點:參考型別欄位共享,修改子物件會互相影響。

模式二:實作 ICloneable 手動深拷貝


public class Person : ICloneable
{
    public int Age { get; set; }
    public string Address { get; set; }
    public Name Name { get; set; }

    public object Clone()
    {
        var clone = new Person
        {
            Age = this.Age,
            Address = this.Address, // string 不可變可直接指派
            Name = new Name         // 手動建立新子物件
            {
                FirstName = this.Name.FirstName,
                LastName = this.Name.LastName
            }
        };
        return clone;
    }
}

優點:完全可控、無反射/序列化開銷、效能最好。缺點:維護成本高,欄位增減需同步修改 Clone 方法,巢狀層級深時程式碼膨脹。

模式三:序列化/反序列化 (通用深拷貝)


[Serializable]
public class Person : ICloneable
{
    public int Age { get; set; }
    public string Address { get; set; }
    public Name Name { get; set; }

    public object Clone()
    {
        using (var ms = new MemoryStream())
        {
            var bf = new BinaryFormatter();
            bf.Serialize(ms, this);
            ms.Position = 0;
            return bf.Deserialize(ms);
        }
    }
}

[Serializable]
public class Name { ... }

優點:一次實作支援任意複雜物件圖、自動處理循環參考。缺點:

  • 需標記 [Serializable],所有成員型別皆需可序列化。
  • BinaryFormatter **已被標記為不安全/過時** (.NET 5+ 警告),建議改用 System.Text.Json、protobuf-net、MessagePack 等現代序列化器。
  • 效能較差 (IO、反射、配置大量物件)。

現代 JSON 深拷貝範例


public T DeepClone(T source)
{
    var json = System.Text.Json.JsonSerializer.Serialize(source);
    return System.Text.Json.JsonSerializer.Deserialize(json);
}

模式四:反射通用深拷貝 (可自訂屬性控制)


public static class DeepCloner
{
    public static T Clone(T source) where T : class, new()
    {
        if (source == null) return null;
        var clone = new T();
        CloneObject(source, clone);
        return clone;
    }

    private static void CloneObject(object source, object target)
    {
        var type = source.GetType();
        foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (!prop.CanRead || !prop.CanWrite) continue;
            // 可加入 [Cloneable(CloneableState.Exclude)] 等屬性判斷
            var value = prop.GetValue(source);
            if (value == null) { prop.SetValue(target, null); continue; }

            if (prop.PropertyType.IsValueType || prop.PropertyType == typeof(string))
            {
                prop.SetValue(target, value); // 值型別/string 直接指派
            }
            else if (typeof(IEnumerable).IsAssignableFrom(prop.PropertyType))
            {
                // 集合類型需遞迴複製元素 (略)
            }
            else
            {
                // 遞迴複製參考型別
                var clonedValue = CloneObjectRecursive(value);
                prop.SetValue(target, clonedValue);
            }
        }
    }
    // 遞迴邏輯略...
}

優點:無需修改原始類別、可透過 Attribute 控制拷貝行為 (排除、深/淺)。缺點:反射效能開銷大、需處理循環參考、泛型集合複製複雜。

效能比較參考 (相對數值)

方法相對耗時記憶體配置適用性
MemberwiseClone1x (基準)極低淺拷貝
手動 ICloneable1.5x ~ 3x低深拷貝、熱路徑
JSON 序列化50x ~ 200x高深拷貝、非熱路徑、跨進程
BinaryFormatter100x ~ 500x高**不建議新專案使用**
反射通用20x ~ 100x中高深拷貝、動態型別、Plugin 架構

最佳實務建議

  1. 優先使用不可變型別 (record、readonly struct、string) → 根本不需要拷貝。
  2. 熱路徑 (高頻呼叫):手動實作 ICloneable 或建構函式複製 new Person(original)。
  3. 非熱路徑 / 複雜物件圖:System.Text.Json 序列化深拷貝,簡潔安全。
  4. 避免 BinaryFormatter、ICloneable 介面本身 (文檔建議勿實作,改用具體方法如 DeepClone())。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved