Не получается полностью клонировать лист с ссылочными объектами через рефлексию

Рейтинг: 0Ответов: 1Опубликовано: 29.03.2023

Класс Part является составной частью класса Tablet в виде List<Part>

public class Part
    {
        public string Name { get; init; } = string.Empty;
        public TableType Type { get; init; }
        public double Value { get; set; }
        public Part() { }
        public Part(TableType type, double value)
        {
            Name = type.ToString();
            Type = type;
            Value = value;
        }
        public override string ToString()
            => $"Type: {Type}; Value: {Value}";
    }
    public class Tablet
    {
        public string Name { get; set; } = string.Empty;
        public int TotalSlots { get; set; }
        public List<Part> Parts { get; set; } = new();
        public Tablet() { }
        public Tablet(string name, TableType sizeType, double size, 
                      TableType smthElse, double smthElseVal)
        {
            Name = name;
            Parts.Add(new()
            {
                Name = sizeType.ToString(),
                Type = sizeType,
                Value = size
            });
            Parts.Add(new()
            {
                Name = smthElse.ToString(),
                Type = smthElse,
                Value = smthElseVal
            });
        }
    }

У меня не получается сделать полную копию изначальной "базы данных" tabletDB так, чтобы в дальнейшем ссылочные данные Parts не менялись одновременно у разных объектов класса Tablet Это можно легко исправить, если для каждого нового объекта Tablet напрямую копировать List<Tablet> tabletDB под новым имененем, но я хочу оставить одну базу данных List<Tablet> tabletDB и в конструкторе класса клонировать эту базу.

List<Tablet> tabletDB = new() 
        {
            new Tablet("Test", TableType.Square, 10, TableType.Rectangle, 10),
            new Tablet("Test3", TableType.Square, 100, TableType.Rectangle, 100)
        };

        var tabletCopy = tabletDB.CloneList();

        foreach (var partsOriginal in tabletDB)
        {
            foreach (var partOriginal in partsOriginal.Parts)
            {
                foreach (var partsCloned in tabletCopy)
                {
                    foreach (var partCloned in partsCloned.Parts)
                    {
                        if (ReferenceEquals(partOriginal, partCloned))
                        {
                            Console.WriteLine($"Clone:\n\t {partCloned}" +
                                $"\nOriginal:\n\t {partOriginal}" +
                                $"\n\nSame reference!!!\n");
                        }
                    }
                }
            }
        }
Clone:
         Type: Square; Value: 10
Original:
         Type: Square; Value: 10

Same reference!!!

Clone:
         Type: Rectangle; Value: 10
Original:
         Type: Rectangle; Value: 10

Same reference!!!

Clone:
         Type: Square; Value: 100
Original:
         Type: Square; Value: 100

Same reference!!!

Clone:
         Type: Rectangle; Value: 100
Original:
         Type: Rectangle; Value: 100

Same reference!!!

Вот метод с рефлексией, почему-то лист с вложенным классом не клонируется полностью.

public static List<T> CloneList<T>(this List<T> listToClone)
        {
            Type listType = listToClone.GetType();
            Type elementType = listType.GetGenericArguments()[0];
            List<T> listCopy = new();
            foreach (T item in listToClone)
            {
                object itemCopy = Activator.CreateInstance(elementType)!;
                foreach (PropertyInfo property in elementType.GetProperties())
                {
                    elementType
                    .GetProperty(property.Name)
                    ?.SetValue(itemCopy, property.GetValue(item));
                }
                listCopy.Add((T)itemCopy);
            }
            return listCopy;
        }

Update 3/30/2023

Проблему решил заменой рефлексии на сериализацию:

string jsonString = JsonSerializer.Serialize(tabletDB);
var tabletDatabseFromJson = JsonSerializer.Deserialize<List<Tablet>>(jsonString);

Ответы

▲ 1Принят

Проблему решил заменой рефлексии на сериализацию:

public static List<T> GetItemsFromDatabase<T>(this List<T> database)
        {
            string jsonString = JsonSerializer.Serialize(database);
            return JsonSerializer.Deserialize<List<T>>(jsonString)!;
        }