溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C#深拷貝的方法是什么

發(fā)布時間:2022-04-16 13:35:56 來源:億速云 閱讀:149 作者:iii 欄目:開發(fā)技術(shù)

今天小編給大家分享一下C#深拷貝的方法是什么的相關(guān)知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

測試平臺:Intel 9700K+DDR4 3600 32G,框架為.NET 5.0。測試方式為創(chuàng)建100萬次,比較執(zhí)行時間。拷貝的對象如下:

[Serializable]
class UserInfo
    {
        public string Name { get; set; }

        public string UserId { get; set; }

        public int Age { get; set; }

        public string Address { get; set; }

        public long UpdateTime { get; set; }

        public long CreateTime { get; set; }
    }

1、手寫創(chuàng)建對象

簡單對象創(chuàng)建,不考慮有構(gòu)造函數(shù)的情況。

NewUserInfo newInfo = new NewUserInfo()
{
    Name = info.Name,
    Age = info.Age,
    UserId = info.UserId,
    Address = info.Address,
    UpdateTime = info.UpdateTime,
    CreateTime = info.CreateTime,
};

100萬次執(zhí)行時間為39.4073ms,位居第一。當(dāng)然,在這種不考慮構(gòu)造函數(shù)的情況下,手寫創(chuàng)建肯定是最快的。但是同時,如果遇到復(fù)雜對象,代碼量也是最多的。

2、反射

這也是在日常代碼中最常用的方式之一。

private static TOut TransReflection<TIn, TOut>(TIn tIn)
{
    TOut tOut = Activator.CreateInstance<TOut>();
    var tInType = tIn.GetType();
    foreach (var itemOut in tOut.GetType().GetProperties())
    {
        var itemIn = tInType.GetProperty(itemOut.Name); ;
        if (itemIn != null)
        {
            itemOut.SetValue(tOut, itemIn.GetValue(tIn));
        }
    }
    return tOut;
}

調(diào)用

NewUserInfo newInfo = TransReflection<UserInfo, NewUserInfo>(info);

100萬次執(zhí)行時間為1618.4662ms,平均執(zhí)行時間為0.001618,看起來還行。

3、Json字符串序列化

使用System.Text.Json作為序列化和反序列化工具。

UserInfo newInfo = JsonSerializer.Deserialize<UserInfo>(JsonSerializer.Serialize(info));

100萬次執(zhí)行時間為2222.2078ms,比反射慢一點點。

4、對象二進制序列化

首先不推薦使用這種方式,一是BinaryFormatter.Serialize微軟已不推薦使用(據(jù)微軟官網(wǎng)文檔說是有漏洞,具體有什么漏洞沒細究),二是必須在要序列化的對象上面寫上Serializable的關(guān)鍵字,三是速度并不理想。

private static TOut ObjectMemoryConvert<TIn, TOut>(TIn tIn)
{
    using (MemoryStream ms = new MemoryStream())
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(ms, tIn);
        ms.Position = 0;
        return (TOut)formatter.Deserialize(ms);
    }
}

100萬次執(zhí)行時間為8545.9835ms,講道理應(yīng)該是比Json序列化要更快的,但是實際上慢了許多。

5、AutoMapper

熟悉的AutoMapper,性能也沒有讓我們失望。

//循環(huán)外創(chuàng)建MapperConfig
var config = new MapperConfiguration(cfg => cfg.CreateMap<UserInfo, UserInfo>());
var mapper = config.CreateMapper();

//循環(huán)內(nèi)調(diào)用
UserInfo newInfo = mapper.Map<UserInfo>(info);

100萬次執(zhí)行時間為267.5073ms,位居第三。

6、表達式樹

重頭戲來了,此處代碼來源于文首中的博客中,性能讓人大吃一驚。其原理是反射和表達式樹相結(jié)合,先用反射獲取字段然后緩存起來,再用表達式樹賦值。

public static class TransExp<TIn, TOut>
{
    private static readonly Func<TIn, TOut> cache = GetFunc();
    private static Func<TIn, TOut> GetFunc()
    {
        ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
        List<MemberBinding> memberBindingList = new List<MemberBinding>();

        foreach (var item in typeof(TOut).GetProperties())
        {
            if (!item.CanWrite) continue;
            MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
            MemberBinding memberBinding = Expression.Bind(item, property);
            memberBindingList.Add(memberBinding);
        }

        MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
        Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression });

        return lambda.Compile();
    }

    public static TOut Trans(TIn tIn)
    {
        return cache(tIn);
    }
}

調(diào)用

UserInfo newInfo = TransExp<UserInfo, UserInfo>.Trans(info);

100萬次執(zhí)行時間為77.3653ms,位居第二。僅比手寫慢一點點。

簡單整理成柱狀圖,可以很清晰的對比出這幾種深拷貝方式之間的速度差距??偨Y(jié)來說就是,一般簡單的對象深拷貝,推薦直接手寫,復(fù)雜對象深拷貝,推薦使用表達式樹。當(dāng)然,如果創(chuàng)建對象中還涉及到構(gòu)造函數(shù)初始化,那又是不同的情況,這里暫不討論。

C#深拷貝的方法是什么

附上本次測試用的完整代碼。

using AutoMapper;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text.Json;
using System.Threading.Tasks;

namespace TestObjectDeepCopy
{
    class Program
    {
        static void Main(string[] args)
        {
            UserInfo info = new UserInfo()
            {
                Name = "張三",
                Age = 18,
                UserId = Guid.NewGuid().ToString("N"),
                Address = "銀河系地球中國",
                UpdateTime = 1615888888,
                CreateTime = 1615895454,
            };

            var config = new MapperConfiguration(cfg => cfg.CreateMap<UserInfo, UserInfo>());
            var mapper = config.CreateMapper();

            int count = 1000000;
            Stopwatch sw = new Stopwatch();
            sw.Start();

            for (int i = -0; i < count; i++)
            {
                //手寫 39.4073ms
                //UserInfo newInfo = new UserInfo()
                //{
                //    Name = info.Name,
                //    Age = info.Age,
                //    UserId = info.UserId,
                //    Address = info.Address,
                //    UpdateTime = info.UpdateTime,
                //    CreateTime = info.CreateTime,
                //};

                //反射 1618.4662ms
                //UserInfo newInfo = TransReflection<UserInfo, UserInfo>(info);

                //Json字符串序列化 2222.2078ms
                //UserInfo newInfo = JsonSerializer.Deserialize<UserInfo>(JsonSerializer.Serialize(info));

                //對象二進制序列化 8545.9835ms
                //UserInfo newInfo = ObjectMemoryConvert<UserInfo, UserInfo>(info);

                //表達式樹 77.3653ms
                //UserInfo newInfo = TransExp<UserInfo, UserInfo>.Trans(info);

                //AutoMapper 267.5073ms
                //UserInfo newInfo = mapper.Map<UserInfo>(info);
            }

            Console.WriteLine("總共花費{0}ms.", sw.Elapsed.TotalMilliseconds);
            sw.Stop();

            Console.ReadKey();
        }

        private static TOut TransReflection<TIn, TOut>(TIn tIn)
        {
            TOut tOut = Activator.CreateInstance<TOut>();
            var tInType = tIn.GetType();
            foreach (var itemOut in tOut.GetType().GetProperties())
            {
                var itemIn = tInType.GetProperty(itemOut.Name); ;
                if (itemIn != null)
                {
                    itemOut.SetValue(tOut, itemIn.GetValue(tIn));
                }
            }
            return tOut;
        }

        private static TOut ObjectMemoryConvert<TIn, TOut>(TIn tIn)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(ms, tIn);
                ms.Position = 0;
                return (TOut)formatter.Deserialize(ms);
            }
        }
    }

    public static class TransExp<TIn, TOut>
    {
        private static readonly Func<TIn, TOut> cache = GetFunc();
        private static Func<TIn, TOut> GetFunc()
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(TIn), "p");
            List<MemberBinding> memberBindingList = new List<MemberBinding>();

            foreach (var item in typeof(TOut).GetProperties())
            {
                if (!item.CanWrite) continue;
                MemberExpression property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name));
                MemberBinding memberBinding = Expression.Bind(item, property);
                memberBindingList.Add(memberBinding);
            }

            MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());
            Expression<Func<TIn, TOut>> lambda = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExpression });

            return lambda.Compile();
        }

        public static TOut Trans(TIn tIn)
        {
            return cache(tIn);
        }
    }

    [Serializable]
    class UserInfo
    {
        public string Name { get; set; }

        public string UserId { get; set; }

        public int Age { get; set; }

        public string Address { get; set; }

        public long UpdateTime { get; set; }

        public long CreateTime { get; set; }
    }
}

以上就是“C#深拷貝的方法是什么”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學(xué)習(xí)更多的知識,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI