Глубокая копия списка

Я пытаюсь сделать глубокую копию общего списка, и мне интересно, есть ли другой способ создания метода копирования и фактическое копирование по каждому члену по одному. У меня есть class, который выглядит примерно так:

public class Data { private string comment; public string Comment { get { return comment; } set { comment = value; } } private List traceData; public List TraceData { get { return traceData; } set { traceData = value; } } } 

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

Сначала я попытался получить подмножество списка, используя метод List.GetRange() , но кажется, что под List мелко копируется вместо глубокого копирования. Когда я снова получу подмножество, используя List.GetRange (), я получаю ранее измененные данные, а не необработанные данные, полученные в другом месте.

Может ли кто-нибудь дать мне направление о том, как подойти к этому? Большое спасибо.

    Идиоматический подход к этому в C # заключается в том, чтобы реализовать ICloneable в ваших Data и написать метод Clone который делает глубокую копию (а затем, предположительно, метод Enumerable.CloneRange который может клонировать часть вашего списка одновременно). любой встроенный трюк или каркасный метод, чтобы сделать его проще.

    Если память и производительность не представляют реальной проблемы, я предлагаю вам попробовать переделать ее, чтобы работать с неизменяемыми объектами Data , но вместо этого. Это будет намного проще.

    Вы можете попробовать это

      public static object DeepCopy(object obj) { if (obj == null) return null; Type type = obj.GetType(); 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(DeepCopy(array.GetValue(i)), i); } return Convert.ChangeType(copied, obj.GetType()); } else if (type.IsClass) { object toret = Activator.CreateInstance(obj.GetType()); FieldInfo[] fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (FieldInfo field in fields) { object fieldValue = field.GetValue(obj); if (fieldValue == null) continue; field.SetValue(toret, DeepCopy(fieldValue)); } return toret; } else throw new ArgumentException("Unknown type"); } 

    Благодаря статье DetoX83 о проекте кода.

    Если IClonable путь слишком сложный для вас. Я предлагаю конвертировать что-то и обратно. Это можно сделать с помощью BinaryFormatter или Json Converter, такого как Servicestack.Text, поскольку он является самым быстрым в .Net.

    Код должен быть примерно таким:

     MyClass mc = new MyClass(); string json = mc.ToJson(); MyClass mcCloned = json.FromJson(); 

    mcCloned не будет ссылаться на mc.

    Самый простой (но грязный) способ – реализовать ICloneable вашим classом и использовать следующий метод расширения:

     public static IEnumerable Clone(this IEnumerable collection) where T : ICloneable { return collection.Select(item => (T)item.Clone()); } 

    Применение:

     var list = new List { new Data { Comment = "comment", TraceData = new List { 1, 2, 3 } }; var newList = list.Clone(); 

    Другая вещь, которую вы можете сделать, это отметить ваш class как serializable и использовать двоичную сериализацию. Вот рабочий пример

      public class Program { [Serializable] public class Test { public int Id { get; set; } public Test() { } } public static void Main() { //create a list of 10 Test objects with Id's 0-10 List firstList = Enumerable.Range(0,10).Select( x => new Test { Id = x } ).ToList(); using (var stream = new System.IO.MemoryStream()) { var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); binaryFormatter.Serialize(stream, firstList); //serialize to stream stream.Position = 0; //deserialize from stream. List secondList = binaryFormatter.Deserialize(stream) as List; } Console.ReadKey(); } } 

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

     var toPlot = list.Where(d => d.ShouldBePlotted()); 

    Поскольку ваша коллекция изменена, вам необходимо реализовать программный код:

     public class Data { public string Comment { get; set; } public List TraceData { get; set; } public Data DeepCopy() { return new Data { Comment = this.Comment, TraceData = this.TraceData != null ? new List(this.TraceData) : null; } } } 

    Поле « Comment может быть мелким, потому что его уже неизменный class. Вам нужно создать новый список для TraceData , но сами элементы неизменяемы и не требуют специальной обработки для их копирования.

    Когда я снова получу подмножество, используя List.GetRange (), я получаю ранее измененные данные, а не необработанные данные, полученные в другом месте.

    Используйте свой новый метод DeepCopy как таковой:

     var pointsInRange = dataPoints .Select(x => x.DeepCopy()) .GetRange(start, length); 
     using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DeepListCopy_testingSome { class Program { static void Main(string[] args) { List list1 = new List(); List list2 = new List(); //populate list1 for (int i = 0; i < 20; i++) { list1.Add(1); } /////// Console.WriteLine("\n int in each list1 element is:\n"); /////// foreach (int i in list1) { Console.WriteLine(" list1 elements: {0}", i); list2.Add(1); } /////// Console.WriteLine("\n int in each list2 element is:\n"); /////// foreach (int i in list2) { Console.WriteLine(" list2 elements: {0}", i); } ///////enter code here for (int i = 0; i < list2.Count; i++) { list2[i] = 2; } /////// Console.WriteLine("\n Printing list1 and list2 respectively to show\n" + " there is two independent lists,ie, two differens" + "\n memory locations after modifying list2\n\n"); foreach (int i in list1) { Console.WriteLine(" Printing list1 elements: {0}", i); } /////// Console.WriteLine("\n\n"); /////// foreach (int i in list2) { Console.WriteLine(" Printing list2 elements: {0}", i); } Console.ReadKey(); }//end of Static void Main }//end of class } 

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

    метод

     public static List DeepClone(this IList list, bool ignoreVirtualProps = false) { JsonSerializerSettings settings = new JsonSerializerSettings(); if (ignoreVirtualProps) { settings.ContractResolver = new IgnoreNavigationPropsResolver(); settings.PreserveReferencesHandling = PreserveReferencesHandling.None; settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; settings.Formatting = Formatting.Indented; } var serialized = JsonConvert.SerializeObject(list, settings); return JsonConvert.DeserializeObject>(serialized); } 

    Применение

     var clonedList = list.DeepClone(); 

    По умолчанию JSON.NET сериализует только общедоступные свойства. Если частные свойства также должны быть клонированы, это решение можно использовать.

    Этот метод позволяет быстро (де) сериализовать сложные иерархии объектов .

    Interesting Posts
    Давайте будем гением компьютера.