2012-05-16 5 views
0

У меня есть класс C# под названием «SmallClass».deep clone a Список объектов в C#

У меня есть существующий список MyList, содержащий объекты типа «SmallClass»

Я хочу глубокий клон списка «MyList». То есть, глубокий клонировать содержащий список и глубоко клонировать объекты, содержащиеся в списке.

Как это сделать.

public class SmallClass: ICloneable { 

    public string str1; 
    public string str2; 
    public string str3; 

    public SmallClass Clone() //This just deep clones 1 object of type "SmallClass" 
      { 
       MemoryStream m = new MemoryStream(); 
       BinaryFormatter b = new BinaryFormatter(); 
       b.Serialize(m, this); 
       m.Position = 0; 
       return (SRO)b.Deserialize(m); 
      } 

     public override equals(Object a) 
     { 
       return Object.Equals(this.str1 && a.str1); 
      } 
    } 

    public class AnotherClass 
    { 
      SomeCode(); 
      List<SmallClass> myList = new List<SmallList>(); //myList is initialized. 


      // NOW I want to deep clone myList. deep Clone the containing list and deep clone the objects contained in the list. 

     List<SmallClass> newList = new List<SmallClass>(); 
     foreach(var item in myList) 
     { 
      newList.Add((SmallClass)item.Clone()); 
     }  

}

+0

Что здесь происходит? 'return (a.boolean1 && a.boolean1);' – climbage

ответ

4

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

public static T DeepClone<T>(T obj) 
{ 
    using (var stream = new MemoryStream()) 
    { 
     var formatter = new BinaryFormatter(); 
     formatter.Serialize(stream, obj); 
     stream.Position = 0; 
     return (T)formatter.Deserialize(stream); 
    } 
} 

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

List<SmallClass> myListClone = DeepClone(myList); 

наиболее важным фактором вам нужно обратить внимание на то, что все ваши классы должны быть помечены как сериализации, как правило, через [SerializableAttribute].

[SerializableAttribute] 
public class SmallClass 
{ 
    // … 
} 
+0

класс интерфейса ICloneable - метод интерфейса «Clone» - поэтому, если бы я должен был наследовать от ICloneable - я вызову метод «Clone» вместо DeepClone() – User1

+0

«BinaryFormatter» позаботится о глубоком клонировании всего вашего графа объектов, не требуя, чтобы вы продолжали называть методы «Clone» постепенно. Единственное ограничение состоит в том, что ваши пользовательские классы должны быть помечены как '[SerializableAttribute]'. – Douglas

+0

Вы имеете в виду, что просто вызов «DeepClone (myList)» автоматически позаботится о глубоком копировании любых ссылок на вложенные объекты в myList? – User1

4

Ваш SmallClass должен реализовать интерфейс ICloneable. Затем скопируйте каждый элемент с помощью метода Clone().

List<SmallClass> newList = new List<SmallClass>(); 
foreach(var item in myList) 
{ 
    newList.Add((SmallClass)item.Clone()); 
} 
+0

Должен ли я предоставить новую реализацию для реализации клона, унаследованного от ICloneable в «SmallClass»? – User1

+0

Да, ICloneable - это просто интерфейс, а это означает, что он не имеет реализации бы то ни было. Вы должны сами реализовать метод Clone(). Это то, что я имею в виду, когда говорю «Внедрить интерфейс». – BTownTKD

+0

Хорошо. Я добавляю ваше предложение к моему оригинальному сообщению. Кажется, что у кода нет ничего функционально? SmallClass не содержит членов класса. Мне не нужно думать об их клонировании. – User1

0

Есть несколько способов создания глубокой копии, которые включают в себя сериализацию и с использованием методы Object.MemberwiseClone(). Поскольку пример использования сериализации уже доступен здесь, у меня есть подход с использованием «MemberwiseClone».

ПРИМЕЧАНИЕ: Рекурсивная, Платформа: .NETStandard2.0

 /// <summary> 
     /// Returns a deep copy of an object. 
     /// </summary> 
     /// <typeparam name="T"></typeparam> 
     /// <param name="source"></param> 
     /// <returns></returns> 
     public static T DeepClone<T>(this T source) where T : class 
     { 
      if(source == null) return null; 

      if(source is ICollection<object> col) 
      { 
       return (T)DeepCloneCollection(col); 
      } 
      else if(source is IDictionary dict) 
      { 
       return (T)DeepCloneDictionary(dict); 
      } 

      MethodInfo method = typeof(object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance); 
      T clone = (T)method.Invoke(source, null); 

      foreach(FieldInfo field in source.GetType().GetRuntimeFields()) 
      { 
       if(field.IsStatic) continue; 
       if(field.FieldType.GetTypeInfo().IsPrimitive) continue; 

       object sourceValue = field.GetValue(source); 
       field.SetValue(clone, DeepClone(sourceValue)); 
      } 

      return clone; 
     } 

     private static ICollection<object> DeepCloneCollection(ICollection<object> col) 
     { 
      object[] arry = (object[])Activator.CreateInstance(col.GetType(), new object[] { col.Count }); 

      for(int i = 0; i < col.Count; i++) 
      { 
       object orig = col.ElementAt(i); 
       object cln = DeepClone(orig); 

       arry[i] = cln; 
      } 

      return arry; 
     } 

     private static IDictionary DeepCloneDictionary(IDictionary dict) 
     { 
      IDictionary clone = (IDictionary)Activator.CreateInstance(dict.GetType()); 

      foreach(object pair in dict) 
      { 
       object key = pair.GetValueOf("Key"); 
       object original = pair.GetValueOf("Value"); 

       clone.Add(key, original.DeepClone()); 
      } 

      return clone; 
     } 

     public static dynamic GetValueOf<T>(this T value, string property) 
     { 
      PropertyInfo p = value.GetType().GetTypeInfo().GetProperty(property); 

      if(p != null && p.CanRead) 
      { 
       dynamic val = p.GetValue(value); 

       return val; 
      } 

      return Activator.CreateInstance(p.PropertyType); //Property does not have value, return default 
     }