Глубокая копия списка <T>

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

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

    private List<double> traceData;
    public List<double> TraceData
    {
        get { return traceData; }
        set { traceData = value; }
    }
}

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

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

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

+14
источник поделиться
9 ответов

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

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

+10
источник

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

    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 article в проекте кода.

+6
источник
другие ответы

Связанные вопросы


Похожие вопросы

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

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

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

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

+5
источник

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

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

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

var list = new List<Data> { new Data { Comment = "comment", TraceData = new List { 1, 2, 3 } };
var newList = list.Clone();
+3
источник

еще одна вещь, которую вы можете сделать, это отметить ваш класс как 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 0-10
            List<Test> 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<Test> secondList = binaryFormatter.Deserialize(stream) as List<Test>; 
            }


            Console.ReadKey();
        }
    }
+1
источник

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

var toPlot = list.Where(d => d.ShouldBePlotted());
0
источник

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

public class Data
{
    public string Comment { get; set; }
    public List<double> TraceData { get; set; }

    public Data DeepCopy()
    {
        return new Data
        {
            Comment = this.Comment, 
            TraceData = this.TraceData != null
                ? new List<double>(this.TraceData)
                : null;
        }
    }
}

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

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

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

var pointsInRange = dataPoints
    .Select(x => x.DeepCopy())
    .GetRange(start, length);
0
источник
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<int> list1 = new List<int>();
            List<int> list2 = new List<int>();

            //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,i e, 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
}
0
источник

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

Метод

public static List<T> DeepClone<T>(this IList<T> 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<List<T>>(serialized);
}

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

var clonedList = list.DeepClone();

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

Этот метод позволяет быструю (де) сериализацию complex иерархии объектов.

0
источник

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