解讀 --- 深拷貝

来源:https://www.cnblogs.com/pandefu/archive/2023/08/13/17536258.html
-Advertisement-
Play Games

## 引言 深拷貝是指創建一個新對象,該對象的值與原始對象完全相同,但在記憶體中具有不同的地址。這意味著如果您對原始對象進行更改,則不會影響到複製的對象 常見的C#常見的深拷貝方式有以下4類: 1. 各種形式的序列化及反序列化。 2. 通過反射機制獲取該對象的所有欄位和屬性信息。遍歷所有欄位和屬性,遞 ...


引言

深拷貝是指創建一個新對象,該對象的值與原始對象完全相同,但在記憶體中具有不同的地址。這意味著如果您對原始對象進行更改,則不會影響到複製的對象

常見的C#常見的深拷貝方式有以下4類:

  1. 各種形式的序列化及反序列化。
  2. 通過反射機制獲取該對象的所有欄位和屬性信息。遍歷所有欄位和屬性,遞歸將源對象中的值複製到目標對象中。
  3. 新建對象,手動複製所有成員變數。
  4. 實現 ICloneable 介面,重寫 Colne 方法。方法內部可以調用上面任意實現方法。

序列化、反序列化

使用二進位序列化和反序列化

可以使用 BinaryFormatter 類將對象序列化成二進位形式並保存到文件或記憶體流中,然後再使用 BinaryFormatter 反序列化對象,這樣就可以得到該對象的一個完全獨立的副本。

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public static T DeepCopy<T>(T obj)
{
    if (obj == null)
    {
        return default(T);
    }

    MemoryStream stream = new MemoryStream();
    BinaryFormatter formatter = new BinaryFormatter();
    formatter.Serialize(stream, obj);
    stream.Seek(0, SeekOrigin.Begin);
    T copy = (T)formatter.Deserialize(stream);
    stream.Close();

    return copy;
}

使用 XML 序列化和反序列化

可以使用 XmlSerializer 類將對象序列化成 XML 形式並保存到文件或記憶體流中,然後再使用 XmlSerializer 反序列化對象,這樣也可以得到該對象的一個完全獨立的副本。

using System.IO;
using System.Xml.Serialization;

public static T DeepCopy<T>(T obj)
{
    if (obj == null)
    {
        return default(T);
    }

    XmlSerializer serializer = new XmlSerializer(typeof(T));
    MemoryStream stream = new MemoryStream();
    serializer.Serialize(stream, obj);
    stream.Seek(0, SeekOrigin.Begin);
    T copy = (T)serializer.Deserialize(stream);
    stream.Close();

    return copy;
}

使用 DataContractSerializer 序列化和反序列化

可以使用 DataContractSerializer 類將對象序列化成 XML 或二進位形式並保存到文件或記憶體流中,然後再使用 DataContractSerializer 反序列化對象。

using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization.Formatters.Binary;

public static T DeepCopy<T>(T obj)
{
    if (obj == null)
    {
        return default(T);
    }

    DataContractSerializer serializer = new DataContractSerializer(typeof(T));
    MemoryStream stream = new MemoryStream();
    serializer.WriteObject(stream, obj);
    stream.Seek(0, SeekOrigin.Begin);
    T copy = (T)serializer.ReadObject(stream);
    stream.Close();

    return copy;
}

使用 Json.NET 或 System.Text.Json 序列化和反序列化

可以使用 JsonConvert 類將對象序列化成 JSON 字元串,然後再使用 JsonConvert 反序列化對象。

using Newtonsoft.Json;

public static T DeepCopy<T>(T obj)
{
    if (obj == null)
    {
        return default(T);
    }

    string json = JsonConvert.SerializeObject(obj);
    T copy = JsonConvert.DeserializeObject<T>(json);

    return copy;
}
using System.Text.Json;
public static T DeepCopy<T>(T obj)
{
    if (obj == null)
    {
        return default(T);
    }

    string jsonString = JsonSerializer.Serialize<T>(obj);
    // 將 JSON 字元串反序列化為對象
    var deserializedPerson = JsonSerializer.Deserialize<T>(jsonString);

    return deserializedPerson;
}

反射

使用反射實現深拷貝

通過反射生成對象,通過反射機制獲取該對象的所有欄位和屬性信息。遍歷所有欄位和屬性,以遞歸方式將源對象中的值複製到目標對象中。

using System;
using System.Reflection;

public static T DeepCopy<T>(T obj)
{
    if (obj == null)
    {
        return default(T);
    }

    Type type = obj.GetType();
    object copy = Activator.CreateInstance(type);

    // 獲取所有欄位和屬性信息,並將源對象中的值複製到目標對象中
    foreach (FieldInfo fieldInfo in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
    {
        object value = fieldInfo.GetValue(obj);
        fieldInfo.SetValue(copy, DeepCopy(value));
    }
    foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
    {
        if (!propertyInfo.CanWrite || !propertyInfo.CanRead)
        {
            continue;
        }
        object value = propertyInfo.GetValue(obj);
        propertyInfo.SetValue(copy, DeepCopy(value));
    }

    return (T)copy;
}

手動賦值

手動複製所有成員變數

可以手動編寫代碼複製對象中的所有成員變數,這需要對對象結構有很好的瞭解,並且比較繁瑣,容易漏掉某些成員。

public class Person
{
    public string Name;
    public int Age;

    public Person DeepCopy()
    {
        Person copy = new Person();
        copy.Name = this.Name;
        copy.Age = this.Age;
        return copy;
    }
}

ICloneable 介面

實現 ICloneable 介面

可以在對象中實現 ICloneable 介面,並重寫 Clone 方法來實現深拷貝。重寫的 Clone 方法內可以調用上述任何一種方案。

public class Person : ICloneable
{
    public string Name;
    public int Age;

    public object Clone()
    {
        Person copy = new Person();
        copy.Name = this.Name;
        copy.Age = this.Age;
        return copy;
    }
}

第三方庫

還有一種方式是使用第三方庫實現深拷貝,例如 AutoMapper、ValueInjecter 等。這些庫可以自動複製對象中的所有成員變數,從而實現深拷貝。
其中比較常用的包括:

  • AutoMapper:這是一個非常流行的對象映射庫,可以用於將一個對象的屬性值複製到另一個對象中,從而實現對象深拷貝。

  • Newtonsoft.Json:這是一個廣泛使用的 JSON 序列化/反序列化庫,它也提供了一些方法來實現對象深拷貝。

  • Cloneable:這是一個專門為 .NET 平臺設計的對象克隆庫,它提供了多種深拷貝和淺拷貝的方式。

  • FastDeepCloner:這是一個高性能的對象複製庫,它支持對任意類型進行深拷貝,並且提供了多種可配置選項。

可以需要根據自己的具體需求選擇適合自己的庫。如果只是需要簡單的深拷貝操作,那麼 AutoMapper 和 Newtonsoft.Json 都是不錯的選擇;如果需要更加高效、靈活的操作,那麼可以考慮使用 FastDeepCloner 或 Cloneable 等庫。

作者: Niuery Daily

出處: https://www.cnblogs.com/pandefu/>

郵箱: [email protected]

關於作者:.Net Framework,.Net Core ,WindowsForm,WPF ,控制項庫,多線程

本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出 原文鏈接,否則保留追究法律責任的權利。 如有問題, 可郵件咨詢。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • CQRS是Command Query Responsibility Segregation的縮寫,一般稱作命令查詢職責分離。從字面意思理解,就是將命令(寫入)和查詢(讀取)的責任劃分到不同的模型中。 對比一下常用的 CRUD 模式(創建-讀取-更新-刪除),通常我們會讓用戶界面與負責所有四種操作的數... ...
  • ![](https://img2023.cnblogs.com/blog/3076680/202308/3076680-20230811224443622-1444719159.png) # 1. 部署行為是系統生命的重要組成部分 ## 1.1. 只編寫代碼是不夠的,只要沒有在生產環境中運行,一切都 ...
  • [TOC] ## 本篇概要 搭建go語言環境,除了要搭建go語言的編譯環境,還要搭建go語言的集成開發環境,為此需要選擇go語言的集成開發環境的工具,這就是“工於善其事,必先利其器”,可以大大加快自己的開發進度。 ## 集成開發環境工具(ide) 這裡我主要介紹從我2016年開始學習go語言以來使用 ...
  • ### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 起因是懶 - 最近在開發中要用到PostgreSQL數 ...
  • Java技術體系中所提倡的自動記憶體管理最終可以歸結為自動化地解決了兩個問題:給對象分配記憶體以及回收分配給對象的記憶體。關於回收記憶體這一點,我們已經使用了大量篇幅去介紹虛擬機中的垃圾收集器體系以及運作原理,現在我們再一起來探討一下給對象分配記憶體的那點事兒。對象的記憶體分配,往大方向講,就是在堆上分配,對象... ...
  • # MyBatis--1.快速入門 ## MyBatis簡介 ### 原始jdbc操作的弊端 1. 創建、釋放頻繁導致系統資源浪費 2. sql語句在代碼中硬編碼,不易維護 3. 查詢操作時,需要手動將結果集中的數據封裝到實體中。插入操作同理需要手動 解決方案: 1. 使用資料庫連接池初始化連接資源 ...
  • ## Eureka 簡介 Eureka 是一個基於 REST 的服務發現組件,SpringCloud 將它集成在其子項目 spring-cloud-netflix 中,以實現 SpringCloud 的服務註冊與發現,同時提供了負載均衡、故障轉移等能力,目前 Eureka2.0 已經不再維護,故不推 ...
  • Quartz由Java編寫的功能豐富的開源作業調度框架,可以集成到幾乎任何Java應用程式中,並且能夠創建多個作業調度; ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...