有没有更好的方法在 C# 中创建深克隆和浅克隆?

2024-01-20

我一直在为一个项目创建对象,在某些情况下我必须为此对象创建深层副本,我想出了使用 C# 的内置函数 MemberwiseClone()。困扰我的问题是,每当我创建一个新类时,我就必须编写一个像下面的代码这样的函数来进行浅拷贝。有人可以帮我改进这部分并给我一个更好的浅拷贝吗比第二行代码。谢谢 :)

浅拷贝:

public static RoomType CreateTwin(RoomType roomType)
{
    return (roomType.MemberwiseClone() as RoomType);
}

深复制:

public static T CreateDeepClone<T>(T source)
{
    if (!typeof(T).IsSerializable)
    {
        throw new ArgumentException("The type must be serializable.", "source");
    }

    if (Object.ReferenceEquals(source, null))
    {
        return default(T);
    }

    IFormatter formatter = new BinaryFormatter();
    Stream stream = new MemoryStream();
    using (stream)
    {
        formatter.Serialize(stream, source);
        stream.Seek(0, SeekOrigin.Begin);
        return (T)formatter.Deserialize(stream);
    }
}

MemberwiseClone 不是进行深度复制的好选择(MSDN http://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx):

MemberwiseClone 方法通过创建新的对象来创建浅拷贝 对象,然后将当前对象的非静态字段复制到 新对象。如果字段是值类型,则该字段的逐位副本 场进行。如果字段是引用类型,则引用为 已复制但引用的对象未复制;因此,原来的 对象及其克隆引用同一个对象。

这意味着如果克隆对象具有引用类型公共字段或属性,它们将引用与原始对象的字段/属性相同的内存位置,因此克隆对象中的每个更改都将反映在初始对象中。这不是真正的深复制。

您可以使用 BinarySerialization 创建完全独立的对象实例,请参见 MSDN 页面BinaryFormatter 类 http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter.aspx一个序列化的例子。


示例和测试工具:

创建给定对象的深层副本的扩展方法:

public static class MemoryUtils
{
    /// <summary>
    /// Creates a deep copy of a given object instance
    /// </summary>
    /// <typeparam name="TObject">Type of a given object</typeparam>
    /// <param name="instance">Object to be cloned</param>
    /// <param name="throwInCaseOfError">
    /// A value which indicating whether exception should be thrown in case of
    /// error whils clonin</param>
    /// <returns>Returns a deep copy of a given object</returns>
    /// <remarks>Uses BInarySerialization to create a true deep copy</remarks>
    public static TObject DeepCopy<TObject>(this TObject instance, bool throwInCaseOfError)
        where TObject : class
    {
        if (instance == null)
        {
            throw new ArgumentNullException("instance");
        }

        TObject clonedInstance = default(TObject);

        try
        {
            using (var stream = new MemoryStream())
            {
                BinaryFormatter binaryFormatter = new BinaryFormatter();
                binaryFormatter.Serialize(stream, instance);

                // reset position to the beginning of the stream so
                // deserialize would be able to deserialize an object instance
                stream.Position = 0;

                clonedInstance = (TObject)binaryFormatter.Deserialize(stream);
            }
        }
        catch (Exception exception)
        {
            string errorMessage = String.Format(CultureInfo.CurrentCulture,
                            "Exception Type: {0}, Message: {1}{2}",
                            exception.GetType(),
                            exception.Message,
                            exception.InnerException == null ? String.Empty :
                            String.Format(CultureInfo.CurrentCulture,
                                        " InnerException Type: {0}, Message: {1}",
                                        exception.InnerException.GetType(),
                                        exception.InnerException.Message));
            Debug.WriteLine(errorMessage);

            if (throwInCaseOfError)
            {
                throw;
            }
        }

        return clonedInstance;
    }
}

NUnit 测试:

public class MemoryUtilsFixture
{
    [Test]
    public void DeepCopyThrowWhenCopyInstanceOfNonSerializableType()
    {
        var nonSerializableInstance = new CustomNonSerializableType();
        Assert.Throws<SerializationException>(() => nonSerializableInstance.DeepCopy(true));
    }

    [Test]
    public void DeepCopyThrowWhenPassedInNull()
    {
        object instance = null;
        Assert.Throws<ArgumentNullException>(() => instance.DeepCopy(true));
    }

    [Test]
    public void DeepCopyThrowWhenCopyInstanceOfNonSerializableTypeAndErrorsDisabled()
    {
        var nonSerializableInstance = new CustomNonSerializableType();            
        object result = null;

        Assert.DoesNotThrow(() => result = nonSerializableInstance.DeepCopy(false));
        Assert.IsNull(result);
    }

    [Test]
    public void DeepCopyShouldCreateExactAndIndependentCopyOfAnObject()
    {
        var instance = new CustomSerializableType
                        {
                            DateTimeValueType =
                                DateTime.Now.AddDays(1).AddMilliseconds(123).AddTicks(123),
                            NumericValueType = 777,
                            StringValueType = Guid.NewGuid().ToString(),
                            ReferenceType =
                                new CustomSerializableType
                                    {
                                        DateTimeValueType = DateTime.Now,
                                        StringValueType = Guid.NewGuid().ToString()
                                    }
                        };

        var deepCopy = instance.DeepCopy(true);

        Assert.IsNotNull(deepCopy);
        Assert.IsFalse(ReferenceEquals(instance, deepCopy));
        Assert.That(instance.NumericValueType == deepCopy.NumericValueType);
        Assert.That(instance.DateTimeValueType == deepCopy.DateTimeValueType);
        Assert.That(instance.StringValueType == deepCopy.StringValueType);
        Assert.IsNotNull(deepCopy.ReferenceType);
        Assert.IsFalse(ReferenceEquals(instance.ReferenceType, deepCopy.ReferenceType));
        Assert.That(instance.ReferenceType.DateTimeValueType == deepCopy.ReferenceType.DateTimeValueType);
        Assert.That(instance.ReferenceType.StringValueType == deepCopy.ReferenceType.StringValueType);
    }

    [Serializable]
    internal sealed class CustomSerializableType
    {            
        public int NumericValueType { get; set; }
        public string StringValueType { get; set; }
        public DateTime DateTimeValueType { get; set; }

        public CustomSerializableType ReferenceType { get; set; }
    }

    public sealed class CustomNonSerializableType
    {            
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

有没有更好的方法在 C# 中创建深克隆和浅克隆? 的相关文章

  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • C++ 求二维数组每一行的最大值

    我已经设法用这个找到我的二维数组的每一行的最小值 void findLowest int A Cm int n int m int min A 0 0 for int i 0 i lt n i for int j 0 j lt m j if
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • 如何针对 Nancy 中的 Active Directory 进行身份验证?

    这是一篇过时的文章 但是http msdn microsoft com en us library ff650308 aspx paght000026 step3 http msdn microsoft com en us library
  • 按字典顺序对整数数组进行排序 C++

    我想按字典顺序对一个大整数数组 例如 100 万个元素 进行排序 Example input 100 21 22 99 1 927 sorted 1 100 21 22 927 99 我用最简单的方法做到了 将所有数字转换为字符串 非常昂贵
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • 使用 LINQ 查找列表中特定类型的第一个元素

    使用 LINQ 和 C 在元素列表中查找特定类型的第一个项目的最短表示法是什么 var first yourCollection OfType
  • 线程、进程和 Application.Exit()

    我的应用程序由主消息循环 GUI 和线程 Task Factory 组成 在线程中我调用一些第三方应用程序var p new Process 但是当我调用Application Exit 在消息循环中 我可以看到在线程中启动的进程仍在内存中
  • 我的 strlcpy 版本

    海湾合作委员会 4 4 4 c89 我的程序做了很多字符串处理 我不想使用 strncpy 因为它不会终止 我不能使用 strlcpy 因为它不可移植 只是几个问题 我怎样才能让我的函数正常运行 以确保它完全安全稳定 单元测试 这对于生产来
  • 更改窗口的内容 (WPF)

    我创建了一个简单的 WPF 应用程序 它有两个 Windows 用户在第一个窗口中填写一些信息 然后单击 确定 这会将他们带到第二个窗口 这工作正常 但我试图将两个窗口合并到一个窗口中 这样只是内容发生了变化 我设法找到了这个更改窗口内容时
  • 网络参考共享类

    我用 Java 编写了一些 SOAP Web 服务 在 JBoss 5 1 上运行 其中两个共享一个类 AddressTO Web 服务在我的 ApplycationServer 上正确部署 一切都很顺利 直到我尝试在我的 C 客户端中使用
  • AccessViolationException 未处理

    我正在尝试使用史蒂夫 桑德森的博客文章 http blog stevensanderson com 2010 01 28 editing a variable length list aspnet mvc 2 style 为了在我的 ASP
  • 检查 url 是否指向文件或页面

    我们需要以下内容 如果文件确实是文件 则从 URL 下载该文件 否则 如果它是一个页面 则什么也不做 举个简单的例子 我有以下命令来下载文件 My Computer Network DownloadFile http www wired c
  • 什么是 C 语言的高效工作流程? - Makefile + bash脚本

    我正在开发我的第一个项目 该项目将跨越多个 C 文件 对于我的前几个练习程序 我只是在中编写了我的代码main c并使用编译gcc main c o main 当我学习时 这对我有用 现在 我正在独自开展一个更大的项目 我想继续自己进行编译
  • 作为字符串的动态属性名称

    使用 DocumentDB 创建新文档时 我想设置属性名称动态地 目前我设置SomeProperty 像这样 await client CreateDocumentAsync dbs db colls x new SomeProperty
  • ListDictionary 类是否有通用替代方案?

    我正在查看一些示例代码 其中他们使用了ListDictionary对象来存储少量数据 大约 5 10 个对象左右 但这个数字可能会随着时间的推移而改变 我使用此类的唯一问题是 与我所做的其他所有事情不同 它不是通用的 这意味着 如果我在这里
  • 在 ASP.NET 中将事件冒泡为父级

    我已经说过 ASP NET 中的层次结构 page user control 1 user control 2 control 3 我想要做的是 当控件 3 它可以是任何类型的控件 我一般都想这样做 让用户用它做一些触发回发的事情时 它会向
  • 如何在 C# 中播放在线资源中的 .mp3 文件?

    我的问题与此非常相似question https stackoverflow com questions 7556672 mp3 play from stream on c sharp 我有音乐网址 网址如http site com aud
  • 更改显示的 DPI 缩放大小使 Qt 应用程序的字体大小渲染得更大

    我使用 Qt 创建了一些 GUI 应用程序 我的 GUI 应用程序包含按钮和单选按钮等控件 当我运行应用程序时 按钮内的按钮和字体看起来正常 当我将显示器的 DPI 缩放大小从 100 更改为 150 或 200 时 无论分辨率如何 控件的
  • 不同类型的指针可以互相分配吗?

    考虑到 T1 p1 T2 p2 我们可以将 p1 分配给 p2 或反之亦然吗 如果是这样 是否可以不使用强制转换来完成 或者我们必须使用强制转换 首先 让我们考虑不进行强制转换的分配 C 2018 6 5 16 1 1 列出了简单赋值的约束

随机推荐

  • 如何正确地从 Promise 返回多个值?

    我最近多次遇到某种情况 不知道如何正确解决 假设以下代码 somethingAsync then afterSomething then afterSomethingElse function afterSomething amazingD
  • 颁发身份超级账本作曲家时授权失败

    我阅读了建议检查 docker yaml 的答案 文件路径如下 我确认用户名是admin 密码是adminpw 不幸的是我仍然收到授权失败错误 我用来发布身份的命令 composer identity issue n trade netwo
  • 如何以 1 秒的间隔更新组件?

    我在戴尔面试时被问到这个问题 new Date 给你一个Date使用当前日期 时间初始化的对象 假设我的屏幕右上角显示当前时间 并且有人这样做了
  • InApp 计费验证 Web 服务器 PHP 上的订单

    我正在使用一个简单的 PHP 脚本来验证 Android 订单以解析客户的下载 receipt GET purchaseData billInfo json decode receipt true signature GET dataSig
  • stack --nix build 抱怨 ghc 版本不匹配

    在 NixOS 上构建 Threepenny gui 时stack nix build 我收到错误消息 说我的 ghc 版本错误 然后我尝试了stack nix setup 它不会运行 因为 bash 位于 NixOS 上的意外路径上 这是
  • Matlab 2012a Mex 在 Mountain Lion 上与 Xcode 4.5 配合使用

    我安装了 Mountain Lion 并安装了 Xcode 4 5 和 Matlab 2012a 我安装并运行了 MathWorks 网站上提供的补丁 但仍然收到以下错误 Applications MATLAB R2012a app bin
  • 在 iOS 8 上点击或选择输入字段中的所有内容时,光标超出边框

    IOS8有这个问题 IOS7没有 我有一个输入标签 其内容的长度大于其宽度
  • MMC进程立即关闭,无法链接到Windows窗体

    我想做的最终目标是将 MMC Microsoft 管理控制台 计算机管理管理单元 compmgmt msc 进程嵌入到 Windows 窗体中 或者将其视为模态弹出窗口的解决方法上菜单 现在 我只是想让 mmc exe 本身正常工作 然后再
  • 为什么应该在 LINQ To SQL 存储库保存方法中使用 GetOriginalEntityState()?

    我正在查看 Steven Sanderson 的 Pro ASP NET MVC 2 Framework 一书中的产品存储库中的保存方法示例 public void SaveProduct Product product if new pr
  • Google Cloud SQL 实例无响应,如何获得支持?

    当谈到数据库时 我们希望将管理它们的工作交给专业人士 这就是为什么我们选择 CloudSQL 第二代数据库实例形式的托管解决方案 今天实例停止响应 我单击了重新启动 它已经重新启动了几个小时并且没有响应 我尝试克隆实例 也没有响应 我不知道
  • Rails 3.2 和phonegap

    我当前正在运行一个rails应用程序 我想为其制作一个移动应用程序 我不想学习objective c或java 我读了很多关于phonegap的文章以及如何使用它来制作跨平台Web应用程序 Phonegap编译css htm js Rail
  • 如何在mvc4中区分连接等的调试和发布

    所以我对 MVC4 相当陌生 很多模式对我来说都是新的 然而 我好奇的一件事是关于发布 调试模式的最佳实践 对我来说 实时模式和调试模式之间有很多不同的事情 我希望所有这些都是自动的 这样我就不需要更改任何内容来发布 例如 我在我的存储库
  • 使用 sed 从变量中删除模式

    我正在使用一个脚本 该脚本有一个名为PRODUCT VERSION 该版本带有一个点 例如6 0 我需要删除点并将结果保存在另一个变量中 到目前为止我已经做到了 但它不能正常工作 PRD VER sed s PRODUCT VERSION
  • 从字符串数组创建字符串。最快的方法?

    我有一个字符串数组 A 到 E 我想将它们连接成一个字符串 A B C D E 我应该循环遍历数组还是使用 Join 函数 Dim MyArray 5 as String Dim MyString as String MyArray 1 A
  • 如何从网页添加事件到iCal / Outlook / Gmail?

    包含下载 ics iCal 文件的链接吗 通过电子邮件附加 ics 文件 如何构建文件 哪里可以找到规格 将hCalendar中的数据渲染到页面上有什么好处 那么搜索引擎可以以某种方式索引它吗 Thanks 你的问题的第一部分在谷歌上似乎有
  • PL/SQL中如何使用ifexists-ifnotexists?

    我正在尝试将 ifexists 语句从 SQL Server 转换为 PL SQL 但出现错误 我正在尝试检查是否NAME 1我的中不存在table 1 如果它们不存在 那么我正在检查是否COLUMN NAME NAME 2 存在于我的ta
  • 如何使用 REST API 在 keycloak 中重置用户密码

    我想对我的 Keycloak 服务器进行休息调用 根据文档 这应该很容易 https www keycloak org docs api 10 0 rest api index html executeactionsemail https
  • 支持的视频尺寸 MediaRecorder API android

    我正在尝试使用 mediarecorder 和 mediaprojection api 记录屏幕内容 当我尝试在设备上将视频更改为高清时 录制失败 但在 640 x 480 分辨率下工作正常 所以我的问题是如何获得特定设备上支持的视频分辨率
  • Ionic 模拟 android ERR_CONNECTION_REFUSED localhost:8100

    我尝试在 Android 上模拟我的 Ionic 应用程序 一旦我的应用程序在模拟设备中启动 它就会中断并出现以下错误 应用程序错误 净 ERR CONNECTION REFUSED http 本地主机 8100 http localhos
  • 有没有更好的方法在 C# 中创建深克隆和浅克隆?

    我一直在为一个项目创建对象 在某些情况下我必须为此对象创建深层副本 我想出了使用 C 的内置函数 MemberwiseClone 困扰我的问题是 每当我创建一个新类时 我就必须编写一个像下面的代码这样的函数来进行浅拷贝 有人可以帮我改进这部