2017-02-08 3 views
0

У меня есть класс с ObservableCollection и другими свойствами. Так это выглядит немного как это:Clone ObservableCollection

public class A 
{ 
    public int Id { get; set; } 
    ... 
    public object AValue {get; set;} 
} 

Я хотел бы клон объекта этого класса. AValue Недвижимость может быть ObservableCollection или другим объектом. Когда AValue имеет тип ObservableCollection, и я пытаюсь добавить к нему некоторый объект, кажется, что клон имеет тот же объект, что и оригинал. Если я использую свой метод clone только для AValue Свойство, а не объект класса A, он работает. Для клонирования объектов я пытался использовать:

private object Clone(object obj) 
    { 
     var t = obj.GetType(); 

     if (obj == null) 
      return null; 

     if (t.IsValueType || t == typeof(string)) 
     { 
      return obj; 
     } 
     else if (t.IsGenericType) 
     { 
      if (obj is IList) 
      { 
       var c= typeof(ObservableCollection<>); 
       var cType = type.GetGenericArguments().First(); 
       var gType = c.MakeGenericType(cType); 
       var newC = (IList)Activator.CreateInstance(gType); 

       foreach (var item in (IEnumerable)obj) 
        newC.Add(item); 

       return newC; 
      } 
     } 
     else if (t.IsClass) 
     { 
      object objV = Activator.CreateInstance(type); 
      FieldInfo[] fields = t.GetFields(BindingFlags.Public | 
         BindingFlags.NonPublic | BindingFlags.Instance); 
      foreach (FieldInfo field in fields) 
      { 
       object fieldV = field.GetValue(obj); 
       if (fieldValue == null) 
        continue; 
       field.SetValue(objV, Clone(fieldV)); 
      } 
      return obj; 
     } 
     throw new ArgumentException("Unknown type"); 
    } 
} 

Редактировать спасибо за ответ. Я добавил ваш код @Sefe, но он все еще не работает. Я знаю, что другие типы не клонируются, но я бы хотел сделать это со списками в первую очередь. Я не знаю, где ошибка. Я получаю объект класса A, затем я клонирую объект и отправляю объект класса в диалоговое окно. Я меняю коллекцию, и свойства от клона и оригинала одинаковы. Это прийти, чтобы добавить IClonable интерфейса общего типа списка

ObservableCollection<B> = new ObservableCollection<B>(); 

public class B: Base, ICloneable 
{ 
    ... 
    public object Clone() 
    { 
    return this.MemberwiseClone(); 
    } 

}

Важен, что общий тип может наследовать от другого класса?

+0

У вас есть возможная ошибка в коде. Что относительно общих типов, которые не являются списками? Они не будут клонированы. – Sefe

+0

@Sefe Я тоже наткнулся на это, но в этом случае выбрано исключение, которое прекрасно (возможно, это может быть NotImplementedException, но все же). –

ответ

3

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

foreach (var item in (IEnumerable)obj) { 
    ICloneable cloneable = item as ICloneable; 
    if (cloneable != null) { 
     newC.Add(cloneable.Clone()); 
    } 
    else { 
     newC.Add(item); 
    } 
} 

Использования интерфейса ICloneable является встроенным способом в рамках .NET, и вы можете ожидать, методы framweork в признайте его там, где это необходимо. Так что это было бы предпочтительным способом. Если вы хотите использовать свой собственный метод существующего Clone вместо этого, вы можете назвать это рекурсивно:

foreach (var item in (IEnumerable)obj) { 
    newC.Add(Clone(item)); 
} 

UPDATE (Чтобы адресовать редактировать на вопрос в):

При использовании MemberwiseClone, всех поля и события будут переданы клону. MemberwiseClone создает мелкую копию, поэтому передаются поля и назначенные обработчики событий. Поэтому, если у вас есть поля, содержащие другие ссылочные типы, они не будут клонированы (даже если они равны ICloneable). Кроме того, клон будут назначены обработчики событий, которые назначены оригиналу.

Ваш метод ICloneable.Clone должен позаботиться о том, чтобы положить клон в действующее состояние. Поскольку действительность отличается от случая к случаю, в .NET Framework нет автоматического глубокого клонирования. Вызов MemberwiseClone, как правило, хороший старт, но вы должны затем добавить содержащиеся в нем объекты, обработчики событий и т. Д., Чтобы «обновить» ваш мелкий клон до глубокого клона.

+0

Когда мне нужно было делать глубокие копии коллекций, я писал расширение для контейнеров.Подпись - «открытый статический TContainer CloneDeep (этот TContainer r) где T: ICloneable где TContainer: ICollection , new()'. У обычных генераторов все есть 'new()', поэтому он работает для часто встречающихся списков и т. Д. Функция тривиально выполняет итерацию через исходный контейнер и 'Clone()' s элементы, добавляя их в клонированный контейнер. Обратите внимание, что в словаре требуется специальное лечение, которое клонирует ключи и значения. –

Смежные вопросы