Объекты глубокого клонирования

Я хочу сделать что-то вроде:

MyObject myObj = GetMyObj(); // Create and fill a new object
MyObject newObj = myObj.Clone();

И затем внесите изменения в новый объект, который не отражен в исходном объекте.

Мне не часто нужна эта функциональность, поэтому, когда это было необходимо, я прибегал к созданию нового объекта, а затем копировал каждое свойство по отдельности, но это всегда оставляет мне ощущение, что есть лучшее или более элегантное способ справиться с ситуацией.

Как я могу клонировать или глубоко копировать объект так, чтобы клонированный объект мог быть изменен без каких-либо изменений, отражаемых в исходном объекте?

+2105
источник поделиться
45 ответов
  • 1
  • 2

Поскольку почти все ответы на этот вопрос были неудовлетворительными или явно не работают в моей ситуации, я создал AnyClone, который полностью реализован с учетом и решил все потребности здесь. Мне не удалось заставить сериализацию работать в сложном сценарии со сложной структурой, а IClonable не идеален - на самом деле это даже не нужно.

Стандартные атрибуты игнорирования поддерживаются с помощью [IgnoreDataMember], [NonSerialized]. Поддерживает сложные коллекции, свойства без сеттеров, поля только для чтения и т.д.

Я надеюсь, что это поможет кому-то еще, кто столкнулся с теми же проблемами, что и я.

+3
источник

Отказ от ответственности: я являюсь автором упомянутого пакета.

Я был удивлен тем, как в лучших ответах на этот вопрос в 2019 году все еще используются сериализация или рефлексия.

Сериализация ограничена (требуются атрибуты, определенные конструкторы и т.д.) И очень медленная

BinaryFormatter требует атрибута Serializable, JsonConverter требует конструктора или атрибутов без параметров, они не очень хорошо обрабатывают поля или интерфейсы только для чтения, и оба в 10-30 раз медленнее, чем необходимо.

Деревья выражений

Вместо этого вы можете использовать деревья выражений или Reflection.Emit для генерации кода клонирования только один раз, а затем использовать этот скомпилированный код вместо медленного отражения или сериализации.

Сам столкнувшись с проблемой и не найдя удовлетворительного решения, я решил создать пакет, который бы справлялся с этим и работал с каждым типом и почти так же быстро, как пользовательский код.

Вы можете найти проект на GitHub: https://github.com/marcelltoth/ObjectCloner

ИспользованиеВы можете установить его из NuGet. Либо получите пакет ObjectCloner и используйте его как:

var clone = ObjectCloner.DeepClone(original);

или если вы не возражаете загрязнять свой тип объекта расширениями, получите ObjectCloner.Extensions и напишите:

var clone = original.DeepClone();

Performance

PerformanceПростой эталон клонирования иерархии классов показал производительность примерно в 3 раза быстрее, чем с помощью Reflection, в 12 раз быстрее, чем сериализация Newtonsoft.Json, и в 36 раз быстрее, чем рекомендовано BinaryFormatter.

+3
источник

Невероятно, сколько усилий вы можете потратить на интерфейс IClonable - особенно если у вас есть иерархии тяжелых классов. Также MemberwiseClone работает как-то странно - он точно не клонирует даже обычные типы типов типов типа.

И, конечно, наиболее интересной дилеммой для сериализации является сериализация обратных ссылок - например. иерархии классов, где у вас есть отношения между родителями и родителями. Я сомневаюсь, что двоичный сериализатор сможет вам помочь в этом случае. (Это закончится с рекурсивными циклами + переполнение стека).

Мне как-то понравилось предлагаемое решение: Как вы делаете глубокую копию объекта в .NET(С# специально)?

однако - он не поддерживал списки, добавил, что поддержка также учитывает повторное воссоединение. Для правила родительского права, которое я сделал, это поле или свойство должно быть названо "parent", тогда оно будет игнорироваться DeepClone. Возможно, вы захотите решить свои собственные правила для обратных ссылок - для иерархии деревьев это может быть "левое/правое" и т.д.

Вот фрагмент всего кода, включая тестовый код:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;

namespace TestDeepClone
{
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.name = "main_A";
            a.b_list.Add(new B(a) { name = "b1" });
            a.b_list.Add(new B(a) { name = "b2" });

            A a2 = (A)a.DeepClone();
            a2.name = "second_A";

            // Perform re-parenting manually after deep copy.
            foreach( var b in a2.b_list )
                b.parent = a2;


            Debug.WriteLine("ok");

        }
    }

    public class A
    {
        public String name = "one";
        public List<String> list = new List<string>();
        public List<String> null_list;
        public List<B> b_list = new List<B>();
        private int private_pleaseCopyMeAsWell = 5;

        public override string ToString()
        {
            return "A(" + name + ")";
        }
    }

    public class B
    {
        public B() { }
        public B(A _parent) { parent = _parent; }
        public A parent;
        public String name = "two";
    }


    public static class ReflectionEx
    {
        public static Type GetUnderlyingType(this MemberInfo member)
        {
            Type type;
            switch (member.MemberType)
            {
                case MemberTypes.Field:
                    type = ((FieldInfo)member).FieldType;
                    break;
                case MemberTypes.Property:
                    type = ((PropertyInfo)member).PropertyType;
                    break;
                case MemberTypes.Event:
                    type = ((EventInfo)member).EventHandlerType;
                    break;
                default:
                    throw new ArgumentException("member must be if type FieldInfo, PropertyInfo or EventInfo", "member");
            }
            return Nullable.GetUnderlyingType(type) ?? type;
        }

        /// <summary>
        /// Gets fields and properties into one array.
        /// Order of properties / fields will be preserved in order of appearance in class / struct. (MetadataToken is used for sorting such cases)
        /// </summary>
        /// <param name="type">Type from which to get</param>
        /// <returns>array of fields and properties</returns>
        public static MemberInfo[] GetFieldsAndProperties(this Type type)
        {
            List<MemberInfo> fps = new List<MemberInfo>();
            fps.AddRange(type.GetFields());
            fps.AddRange(type.GetProperties());
            fps = fps.OrderBy(x => x.MetadataToken).ToList();
            return fps.ToArray();
        }

        public static object GetValue(this MemberInfo member, object target)
        {
            if (member is PropertyInfo)
            {
                return (member as PropertyInfo).GetValue(target, null);
            }
            else if (member is FieldInfo)
            {
                return (member as FieldInfo).GetValue(target);
            }
            else
            {
                throw new Exception("member must be either PropertyInfo or FieldInfo");
            }
        }

        public static void SetValue(this MemberInfo member, object target, object value)
        {
            if (member is PropertyInfo)
            {
                (member as PropertyInfo).SetValue(target, value, null);
            }
            else if (member is FieldInfo)
            {
                (member as FieldInfo).SetValue(target, value);
            }
            else
            {
                throw new Exception("destinationMember must be either PropertyInfo or FieldInfo");
            }
        }

        /// <summary>
        /// Deep clones specific object.
        /// Analogue can be found here: /questions/2370/how-do-you-do-a-deep-copy-of-an-object-in-net-c-specifically
        /// This is now improved version (list support added)
        /// </summary>
        /// <param name="obj">object to be cloned</param>
        /// <returns>full copy of object.</returns>
        public static object DeepClone(this object obj)
        {
            if (obj == null)
                return null;

            Type type = obj.GetType();

            if (obj is IList)
            {
                IList list = ((IList)obj);
                IList newlist = (IList)Activator.CreateInstance(obj.GetType(), list.Count);

                foreach (object elem in list)
                    newlist.Add(DeepClone(elem));

                return newlist;
            } //if

            if (type.IsValueType || type == typeof(string))
            {
                return obj;
            }
            else if (type.IsArray)
            {
                Type elementType = Type.GetType(type.FullName.Replace("[]", string.Empty));
                var array = obj as Array;
                Array copied = Array.CreateInstance(elementType, array.Length);

                for (int i = 0; i < array.Length; i++)
                    copied.SetValue(DeepClone(array.GetValue(i)), i);

                return Convert.ChangeType(copied, obj.GetType());
            }
            else if (type.IsClass)
            {
                object toret = Activator.CreateInstance(obj.GetType());

                MemberInfo[] fields = type.GetFieldsAndProperties();
                foreach (MemberInfo field in fields)
                {
                    // Don't clone parent back-reference classes. (Using special kind of naming 'parent' 
                    // to indicate child parent class.
                    if (field.Name == "parent")
                    {
                        continue;
                    }

                    object fieldValue = field.GetValue(obj);

                    if (fieldValue == null)
                        continue;

                    field.SetValue(toret, DeepClone(fieldValue));
                }

                return toret;
            }
            else
            {
                // Don't know that type, don't know how to clone it.
                if (Debugger.IsAttached)
                    Debugger.Break();

                return null;
            }
        } //DeepClone
    }

}
+2
источник

При использовании Marc Gravells protobuf-net в качестве вашего сериализатора принятый ответ нуждается в некоторых небольших модификациях, поскольку объект для копирования не будет отнесен к [Serializable] и, следовательно, не будет сериализуемым и метод Clone будет бросать исключение.
Я изменил его для работы с protobuf-net:

public static T Clone<T>(this T source)
{
    if(Attribute.GetCustomAttribute(typeof(T), typeof(ProtoBuf.ProtoContractAttribute))
           == null)
    {
        throw new ArgumentException("Type has no ProtoContract!", "source");
    }

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

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

Это проверяет наличие атрибута [ProtoContract] и использует собственный форматтер protobufs для сериализации объекта.

+2
источник

Картограф выполняет глубокое копирование. Каждый элемент вашего объекта создает новый объект и присваивает все его значения. Он работает рекурсивно на каждом не примитивном внутреннем элементе.

Я предлагаю вам один из самых быстрых, в настоящее время активно развивающихся. Я предлагаю UltraMapper https://github.com/maurosampietro/UltraMapper

Пакеты Nuget: https://www.nuget.org/packages/UltraMapper/

+2
источник

Еще один ответ JSON.NET. Эта версия работает с классами, которые не реализуют ISerializable.

public static class Cloner
{
    public static T Clone<T>(T source)
    {
        if (ReferenceEquals(source, null))
            return default(T);

        var settings = new JsonSerializerSettings { ContractResolver = new ContractResolver() };

        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source, settings), settings);
    }

    class ContractResolver : DefaultContractResolver
    {
        protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
        {
            var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                .Select(p => base.CreateProperty(p, memberSerialization))
                .Union(type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                    .Select(f => base.CreateProperty(f, memberSerialization)))
                .ToList();
            props.ForEach(p => { p.Writable = true; p.Readable = true; });
            return props;
        }
    }
}
+2
источник

Расширение С#, которое будет поддерживать типы "not ISerializable".

 public static class AppExtensions
 {                                                                      
       public static T DeepClone<T>(this T a)
       {
           using (var stream = new MemoryStream())
           {
               var serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));

               serializer.Serialize(stream, a);
               stream.Position = 0;
               return (T)serializer.Deserialize(stream);
           }
       }                                                                    
 }

Использование

       var obj2 = obj1.DeepClone()
+2
источник

Универсальные подходы все технически обоснованы, но я просто хотел добавить примечание от себя, поскольку на самом деле нам редко нужна настоящая глубокая копия, и я бы категорически против использования универсального глубокого копирования в реальных бизнес-приложениях, поскольку это делает его таким, что у вас может быть много места, где объекты копируются и затем явно изменяются, легко потеряться.

В большинстве реальных ситуаций вы также хотите иметь как можно более детальный контроль над процессом копирования, поскольку вы не только связаны с платформой доступа к данным, но и на практике скопированные бизнес-объекты редко бывают одинаковыми на 100%. Рассмотрим пример referenceId, используемый ORM для идентификации ссылок на объекты, полная глубокая копия также будет копировать этот идентификатор, поэтому, пока объекты в памяти будут другими, как только вы отправите их в хранилище данных, они будут жаловаться, поэтому вы будете Необходимо изменить эти свойства вручную после копирования в любом случае, и если объект изменяется, вам необходимо настроить его во всех местах, где используется общее глубокое копирование.

Расширяя ответ @cregox с помощью ICloneable, что на самом деле является глубокой копией? Это просто недавно выделенный объект в куче, который идентичен исходному объекту, но занимает другое место в памяти, как таковое, а не использует общую функциональность клонера. Почему бы просто не создать новый объект?

Я лично использую идею статических фабричных методов в своих объектах домена.

Пример:

    public class Client
    {
        public string Name { get; set; }

        protected Client()
        {
        }

        public static Client Clone(Client copiedClient)
        {
            return new Client
            {
                Name = copiedClient.Name
            };
        }
    }

    public class Shop
    {
        public string Name { get; set; }

        public string Address { get; set; }

        public ICollection<Client> Clients { get; set; }

        public static Shop Clone(Shop copiedShop, string newAddress, ICollection<Client> clients)
        {
            var copiedClients = new List<Client>();
            foreach (var client in copiedShop.Clients)
            {
                copiedClients.Add(Client.Clone(client));
            }

            return new Shop
            {
                Name = copiedShop.Name,
                Address = newAddress,
                Clients = copiedClients
            };
        }
    }

Если кто-то смотрит, как он может структурировать создание объектов, сохраняя полный контроль над процессом копирования, то решение, с которым я лично был очень успешным. Защищенные конструкторы также делают это так, что другие разработчики вынуждены использовать фабричные методы, которые дают аккуратную единую точку создания объекта, инкапсулирующую логику построения внутри объекта. Вы также можете перегрузить метод и при необходимости иметь несколько клонов для разных мест.

+2
источник

Это скопирует все доступные для чтения и записываемые свойства объекта другому.

 public class PropertyCopy<TSource, TTarget> 
                        where TSource: class, new()
                        where TTarget: class, new()
        {
            public static TTarget Copy(TSource src, TTarget trg, params string[] properties)
            {
                if (src==null) return trg;
                if (trg == null) trg = new TTarget();
                var fulllist = src.GetType().GetProperties().Where(c => c.CanWrite && c.CanRead).ToList();
                if (properties != null && properties.Count() > 0)
                    fulllist = fulllist.Where(c => properties.Contains(c.Name)).ToList();
                if (fulllist == null || fulllist.Count() == 0) return trg;

                fulllist.ForEach(c =>
                    {
                        c.SetValue(trg, c.GetValue(src));
                    });

                return trg;
            }
        }

и вот как вы его используете:

 var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave,
                                                            "Creation",
                                                            "Description",
                                                            "IdTicketStatus",
                                                            "IdUserCreated",
                                                            "IdUserInCharge",
                                                            "IdUserRequested",
                                                            "IsUniqueTicketGenerated",
                                                            "LastEdit",
                                                            "Subject",
                                                            "UniqeTicketRequestId",
                                                            "Visibility");

или скопировать все:

var cloned = Utils.PropertyCopy<TKTicket, TKTicket>.Copy(_tmp, dbsave);
+1
источник

как насчет простой перестройки внутри метода который должен вызывать в основном автоматический конструктор копирования

T t = new T();
T t2 = (T)t;  //eh something like that

        List<myclass> cloneum;
        public void SomeFuncB(ref List<myclass> _mylist)
        {
            cloneum = new List<myclass>();
            cloneum = (List < myclass >) _mylist;
            cloneum.Add(new myclass(3));
            _mylist = new List<myclass>();
        }

похоже, работает со мной

+1
источник

Я нашел новый способ сделать это, это Emit.

Мы можем использовать Emit для добавления IL в приложение и запускать его. Но я не думаю, что это хороший способ для того, чтобы я хотел это сделать, чтобы написать свой ответ.

Emit может видеть официальный документ и Guide

Вы должны изучить некоторые IL, чтобы прочитать код. Я напишу код, который может скопировать свойство в класс.

public static class Clone
{        
    // ReSharper disable once InconsistentNaming
    public static void CloneObjectWithIL<T>(T source, T los)
    {
        //see http://lindexi.oschina.io/lindexi/post/C-%E4%BD%BF%E7%94%A8Emit%E6%B7%B1%E5%85%8B%E9%9A%86/
        if (CachedIl.ContainsKey(typeof(T)))
        {
            ((Action<T, T>) CachedIl[typeof(T)])(source, los);
            return;
        }
        var dynamicMethod = new DynamicMethod("Clone", null, new[] { typeof(T), typeof(T) });
        ILGenerator generator = dynamicMethod.GetILGenerator();

        foreach (var temp in typeof(T).GetProperties().Where(temp => temp.CanRead && temp.CanWrite))
        {
            //do not copy static that will except
            if (temp.GetAccessors(true)[0].IsStatic)
            {
                continue;
            }

            generator.Emit(OpCodes.Ldarg_1);// los
            generator.Emit(OpCodes.Ldarg_0);// s
            generator.Emit(OpCodes.Callvirt, temp.GetMethod);
            generator.Emit(OpCodes.Callvirt, temp.SetMethod);
        }
        generator.Emit(OpCodes.Ret);
        var clone = (Action<T, T>) dynamicMethod.CreateDelegate(typeof(Action<T, T>));
        CachedIl[typeof(T)] = clone;
        clone(source, los);
    }

    private static Dictionary<Type, Delegate> CachedIl { set; get; } = new Dictionary<Type, Delegate>();
}

Код может быть глубоким, но он может скопировать свойство. Если вы хотите сделать это до глубокой копии, которую вы можете изменить для IL, это слишком сложно, что я не могу это сделать.

+1
источник

Глубокое клонирование - это копирование состояния. Для .net состояние означает поля.

Допустим, у кого-то есть иерархия:

static class RandomHelper
{
    private static readonly Random random = new Random();

    public static int Next(int maxValue) => random.Next(maxValue);
}

class A
{
    private readonly int random = RandomHelper.Next(100);

    public override string ToString() => $"{typeof(A).Name}.{nameof(random)} = {random}";
}

class B : A
{
    private readonly int random = RandomHelper.Next(100);

    public override string ToString() => $"{typeof(B).Name}.{nameof(random)} = {random} {base.ToString()}";
}

class C : B
{
    private readonly int random = RandomHelper.Next(100);

    public override string ToString() => $"{typeof(C).Name}.{nameof(random)} = {random} {base.ToString()}";
}

Клонирование можно сделать:

static class DeepCloneExtension
{
    // consider instance fields, both public and non-public
    private static readonly BindingFlags bindingFlags =
        BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;

    public static T DeepClone<T>(this T obj) where T : new()
    {
        var type = obj.GetType();
        var result = (T)Activator.CreateInstance(type);

        do
            // copy all fields
            foreach (var field in type.GetFields(bindingFlags))
                field.SetValue(result, field.GetValue(obj));
        // for every level of hierarchy
        while ((type = type.BaseType) != typeof(object));

        return result;
    }
}

Демо1:

Console.WriteLine(new C());
Console.WriteLine(new C());

var c = new C();
Console.WriteLine($"{Environment.NewLine}Image: {c}{Environment.NewLine}");

Console.WriteLine(new C());
Console.WriteLine(new C());

Console.WriteLine($"{Environment.NewLine}Clone: {c.DeepClone()}{Environment.NewLine}");

Console.WriteLine(new C());
Console.WriteLine(new C());

Результат:

C.random = 92 B.random = 66 A.random = 71
C.random = 36 B.random = 64 A.random = 17

Image: C.random = 96 B.random = 18 A.random = 46

C.random = 60 B.random = 7 A.random = 37
C.random = 78 B.random = 11 A.random = 18

Clone: C.random = 96 B.random = 18 A.random = 46

C.random = 33 B.random = 63 A.random = 38
C.random = 4 B.random = 5 A.random = 79

Обратите внимание, что все новые объекты имеют случайные значения для random поля, но clone точно соответствует image

Демо2:

class D
{
    public event EventHandler Event;
    public void RaiseEvent() => Event?.Invoke(this, EventArgs.Empty);
}

// ...

var image = new D();
Console.WriteLine($"Created obj #{image.GetHashCode()}");

image.Event += (sender, e) => Console.WriteLine($"Event from obj #{sender.GetHashCode()}");
Console.WriteLine($"Subscribed to event of obj #{image.GetHashCode()}");

image.RaiseEvent();
image.RaiseEvent();

var clone = image.DeepClone();
Console.WriteLine($"obj #{image.GetHashCode()} cloned to obj #{clone.GetHashCode()}");

clone.RaiseEvent();
image.RaiseEvent();

Результат:

Created obj #46104728
Subscribed to event of obj #46104728
Event from obj #46104728
Event from obj #46104728
obj #46104728 cloned to obj #12289376
Event from obj #12289376
Event from obj #46104728

Обратите внимание, что поле поддержки события тоже копируется, и клиент также подписывается на событие клонирования.

+1
источник

Использование System.Text.Json:

https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/

public static T DeepCopy<T>(this T source)
{
    return source == null ? default : JsonSerializer.Parse<T>(JsonSerializer.ToString(source));
}

Новый API использует Span<T>. Это должно быть быстро, было бы неплохо сделать некоторые тесты.

Примечание: нет необходимости в ObjectCreationHandling.Replace как в Json.NET, поскольку он заменит значения коллекции по умолчанию. Вы должны забыть о Json.NET сейчас, поскольку все будет заменено новым официальным API.

Я не уверен, что это будет работать с частными полями.

0
источник

Быстрый, легкий, эффективный пакет Nuget для решения проблемы клонирования

Прочитав все ответы, я был удивлен, что никто не упомянул этот превосходный пакет:

https://github.com/force-net/DeepCloner

Немного проработав его readme, вот причина, по которой мы выбрали его на работе:

Отказ от ответственности - требования:

  • .NET 4.0 или выше или .NET Standard 1.3 (.NET Core)
  • Требуется набор разрешений "Полное доверие" или "Отражение" (MemberAccess)
  • Это может глубокая или мелкая копия
  • При глубоком клонировании сохраняется весь граф объектов.
  • Использует генерацию кода во время выполнения, так как клонирование результатов происходит невероятно быстро
  • Объекты, скопированные внутренней структурой, никакие методы или вызванные ctors
  • Вам не нужно как-то маркировать классы (например, Serializable-attribute или реализовывать интерфейсы).
  • Нет необходимости указывать тип объекта для клонирования. Объект может быть приведен к интерфейсу или как абстрактный объект (например, вы можете клонировать массив целых чисел как абстрактный Array или IEnumerable; даже нуль можно клонировать без каких-либо ошибок).
  • Клонированный объект не имеет никакой возможности определить, является ли он клоном (за исключением очень специфических методов)

Использование это просто:

  var deepClone = new { Id = 1, Name = "222" }.DeepClone();
  var shallowClone = new { Id = 1, Name = "222" }.ShallowClone();
0
источник

Я знаю, что этот вопрос и answer сидит здесь некоторое время, а следующий - не совсем ответ, а скорее наблюдение, с которым я столкнулся недавно, когда я проверял, действительно, рядовые не клонируются (я бы не был собой, если не был), когда я с радостью скопировал скопированный @johnc обновленный ответ.

Я просто сделал метод расширения (который в значительной степени скопирован в виде вышеупомянутого ответа):

public static class CloneThroughJsonExtension
{
    private static readonly JsonSerializerSettings DeserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };

    public static T CloneThroughJson<T>(this T source)
    {
        return ReferenceEquals(source, null) ? default(T) : JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(source), DeserializeSettings);
    }
}

и упал наивно классом, подобным этому (на самом деле их было больше, но они не связаны):

public class WhatTheHeck
{
    public string PrivateSet { get; private set; } // matches ctor param name

    public string GetOnly { get; } // matches ctor param name

    private readonly string _indirectField;
    public string Indirect => $"Inception of: {_indirectField} "; // matches ctor param name
    public string RealIndirectFieldVaule => _indirectField;

    public WhatTheHeck(string privateSet, string getOnly, string indirect)
    {
        PrivateSet = privateSet;
        GetOnly = getOnly;
        _indirectField = indirect;
    }
}

и код:

var clone = new WhatTheHeck("Private-Set-Prop cloned!", "Get-Only-Prop cloned!", "Indirect-Field clonned!").CloneThroughJson();
Console.WriteLine($"1. {clone.PrivateSet}");
Console.WriteLine($"2. {clone.GetOnly}");
Console.WriteLine($"3.1. {clone.Indirect}");
Console.WriteLine($"3.2. {clone.RealIndirectFieldVaule}");

привело к:

1. Private-Set-Prop cloned!
2. Get-Only-Prop cloned!
3.1. Inception of: Inception of: Indirect-Field cloned!
3.2. Inception of: Indirect-Field cloned!

Я был целым как: ЧТО Ф... поэтому я схватил Newtonsoft.Json Github repo и начал копать. Выходит, что: при десериализации типа, который имеет только один ctor и его имена параметров, соответствуют (нечувствительный к регистру) public property имена они будут переданы в ctor как те параметры. Некоторые подсказки можно найти в коде здесь и здесь.

Нижняя строка

Я знаю, что это скорее не обычный случай, а примерный код немного оскорбителен, но эй! Это застало меня врасплох, когда я проверял, есть ли в кустах дракона, чтобы выпрыгнуть и укусить меня в задницу.;)

-3
источник
  • 1
  • 2

Посмотрите другие вопросы по меткам или Задайте вопрос