【问题标题】:How does MemberWiseClone create a new object with the cloned properties?MemberWiseClone 如何使用克隆的属性创建新对象?
【发布时间】:2017-06-19 07:07:54
【问题描述】:

我想创建一个更受限制的MemberwiseClone 版本,但我意识到我自己的 C# 代码可以向对象添加属性的唯一方法是使用 dynamic,但这不能为对象提供与原本的。我的另一个更丑陋的选择是为新克隆发出源代码并在运行时编译它,但这会带来复杂性。为了简单起见,我不关心的程序集引用等。

现在我只使用MemberwiseClone,但我真的很好奇它是如何工作的。我找不到任何反编译的源代码。

【问题讨论】:

  • 这些天你不需要反编译器。 reference source 将告诉您该方法是由运行时本身实现的(MethodImplOptions.InternalCall)。从那里你前往 CoreClr github repo 并搜索你的电话。在您的情况下,您最终会找到 this file,其中包含您感兴趣的实现 (ObjectNative::Clone)。
  • 谢谢@thehennyy,我现在对如何在参考代码中查找内容有了更多了解,但我的 C++ 落后了我大约十年,我不知道 ObjectNative::Clone 做了什么。我想知道这个问题是使用什么原理来克隆对象。看到它是C++,它是直接字节复制还是什么?
  • 世界上谁能发现这个问题“太宽泛”并投票结束?我在问一个非常具体的问题,只能得到非常有限的答案。标题就是问题;假装阅读。
  • thehennyy 已经找到了该方法的 C++ 源代码。它所做的是为克隆分配新的空对象,然后将内存从当前对象复制到为克隆分配的内存(通过 memcpy 或 memmove)。所以回答你的问题:它通过将内存从旧对象直接复制到新对象来工作。
  • 谢谢@Evk,这是我一直在寻找的答案,有一点细节,比如它到底复制了什么,例如字段和属性?公开和私人等。让您评论答案,并尝试添加这些详细信息,您将获得赏金。

标签: c# dynamic reflection clone


【解决方案1】:

基本上在MemberwiseCloneMSDNdocumentation中有解释:

MemberwiseClone 方法通过创建一个新对象来创建浅拷贝,然后将当前对象的非静态字段复制到新对象。如果字段是值类型,则执行该字段的逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象和它的克隆对象引用的是同一个对象。

上面的实际实现是由CLR内部实现的。您可以将其视为以下标准 C# 代码的优化变体:

using System.Reflection;
using System.Runtime.Serialization;

public static object CustomMemberwiseClone(object source)
{
    var clone = FormatterServices.GetUninitializedObject(source.GetType());
    for (var type = source.GetType(); type != null; type = type.BaseType)
    {
        var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        foreach (var field in fields)
            field.SetValue(clone, field.GetValue(source));
    }
    return clone;
}

即获取实际类型,创建该类型的新实例而不调用任何构造函数,然后从源对象复制每个实例字段值。

再次,实际的内部实现是不同的(如 cmets 中所指出的),上面的 sn-p 只是为了演示原理以及如何通过反射实现具有相同语义的方法(当然会很多比 CLR 方法慢)。

由 OP 添加:请参阅下面的 Ivan 的 cmets,了解我正在寻求的进一步解释。我考虑了他回答的这些部分,并将根据它们接受它。

【讨论】:

  • 谢谢,不过我明白,我什至自己编写了代码,以确保只复制声明的、公共的和非集合属性,而不是字段——我的详细视图——模型应该只有简单的属性,但我要问的是,用高级术语来说,CLR 方法是如何在内部完成的,即它是二进制文件,字节复制字节,还是什么?
  • 基本使用低级拷贝内存(字节)。类似于struct变量赋值。
  • 对内部的 GC Refs(指针)有一些特别的注意,但同样,与复制 struct 和内部的 string 没有什么不同。不管怎样,现在我看到我的回答没有解决你的问题,所以我要删除它。
  • 不,@Ivan,不要删除它,你对它的评论解释得很好,我认为评论是答案的一部分。
  • 很公平,谢谢 :) 我仍然准备好这样做,以防@Evk 决定从他的评论中撰写答案。
猜你喜欢
  • 2016-08-05
  • 1970-01-01
  • 2015-02-08
  • 1970-01-01
  • 2011-05-06
  • 1970-01-01
  • 2016-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多