2009-06-23 2 views
21

Есть ли способ скопировать класс в C#? Что-то вроде var dupe = MyClass (оригинал).копировать класс, C#

+2

Вопрос не ясен. Вы говорите об объектах, а не о классах, и вы должны уточнить, какую семантику вы хотите, как сказал Гроу. Http://stackoverflow.com/questions/184710/what-is-the-difference-between-a-deep-copy- и-a-the-small-copy –

+0

Одна из возможностей - использовать отражение, чтобы создать общий метод для этого: http://pmichaels.net/2016/03/04/dynamically-copy-a-class-from-one -instance-to-another-using-reflection/ –

ответ

18

Не все классы имеют эту функциональность. Вероятно, если класс делает, он предоставляет метод Clone. Чтобы помочь реализовать этот метод для ваших собственных классов, существует защищенный метод MemberwiseClone, определенный в System.Object, который делает мелкую копию текущего экземпляра (т. Е. Поля копируются, если они являются ссылочными типами, ссылка укажет на исходное местоположение).

1

Одна из возможных - clone. Вы должны реализовать интерфейс ICloneable и метод Clone.

+2

Обратите внимание, что рассматриваемый интерфейс устарел. Никогда не было ясно, означает ли это «глубокая копия» или «мелкая копия»; интерфейс с неясным контрактом - это не тот, который может быть точно реализован или надежно использован. (В следующий раз, когда вы создаете базовую библиотеку базового класса, не повторяйте эту ошибку! :)) –

2

Непростой способ, который всегда будет работать. Если ваш класс [Serializable] или реализует ISerializable, вы можете сделать сериализацию в оба конца, чтобы создать идентичную копию. Те же работы для [DataContract]

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

Если вам повезет, класс реализует ICloneable, и вы можете просто вызвать метод Clone().

4

Я думаю, что автор спрашивает о копировании конструкторах ...

ответа «да, но только если вы реализуете это сам», нет «автоматического» способа сделать это без каких-либо тяжелых cludges (читаю Отражение):

class MyClass { 
    private string _name; 

    // standard constructor, so that we can make MyClass's without troubles 
    public MyClass(string name) {_name = name} 
    public MyClass(MyClass old) { 
     _name = old._name; 
     // etc... 
    } 
} 

другая вещь отметить в этом отношении является интерфейс IClonable и метод .Clone() это обеспечивает. Также метод защиты .MemberwiseClone(), предоставляемый классом Object, может помочь реализовать оба этих метода.

1

Вы имеете в виду конструктор копирования, как он существует в C++?

Это не существует в C#. Что вы можете сделать, это написать свой собственный (это утомительно), или вы можете написать метод «Клонировать», который использует сериализацию для создания нового экземпляра, который имеет точно такие же значения, что и исходный класс.

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

35

Возможно, вы говорите о глубокой копии (deep copy vs shallow copy)?

Вы должны либо:

  1. реализации (жесткий код) метод самостоятельно,
  2. пытаются реализовать (или найти) реализацию, которая использует отражение или Выделяют сделать это динамически (объяснение here),
  3. используйте сериализацию и десериализацию для создания глубокой копии, если объект отмечен атрибутом [Serializable].
public static T DeepCopy<T>(T other) 
{ 
    using (MemoryStream ms = new MemoryStream()) 
    { 
     BinaryFormatter formatter = new BinaryFormatter(); 
     formatter.Serialize(ms, other); 
     ms.Position = 0; 
     return (T)formatter.Deserialize(ms); 
    } 
} 

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

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

+0

Сериализация - хорошая идея :-). –

+0

@Martin: Да, это самый простой способ, хотя использовать собственный класс сериализации .NET должен быть помечен как '[Serializable]'. В противном случае вы должны использовать некоторые другие реализации. – Groo

+1

По какой-то причине приведенный фрагмент кода отсутствует . Например, я должен видеть: public static T DeepCopy (T другой) – sapbucket

4

Да и нет. Это область, где вам нужно быть немного осторожным, потому что есть некоторые ловушки (и опять же, это не сложно).

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

Однако здесь находится ловушка: интерфейс ICloneable недостаточно определяет, будет ли ожидаться глубокий клон или мелкий клон, поэтому некоторые реализации делают одно, а другие реализации - другое. По этой причине ICloneable не используется много, и его дальнейшее использование активно обескураживается - см. Отличный Framework Design Guidelines для более подробной информации.

Если вы копаете дальше в BCL, вы можете обнаружить, что System.Object имеет защищенный метод MemberwiseClone. Хотя вы не можете вызвать этот метод напрямую из другого типа, вы можете использовать этот метод для реализации клонирования в ваших собственных объектах.

Общий шаблон клонирования - это определение защищенного конструктора класса, который вы хотите клонировать, и передать уже существующий экземпляр в качестве параметра. Что-то вроде этого:

public class MyClass() 
{ 
    public MyClass() {} 

    protected MyClass(MyClass other) 
    { 
     // Cloning code goes here... 
    } 

    public MyClass Clone() 
    { 
     return new MyClass(this); 
    } 
} 

Однако очевидно, что это работает только в том случае, если вы контролируете тип, который хотите клонировать.

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

1

Простой способ клонировать объект записывает его в поток и читать его снова:

public object Clone() 
{ 
    object clonedObject = null; 
    BinaryFormatter formatter = new BinaryFormatter(); 
    using (Stream stream = new MemoryStream()) 
    { 
     formatter.Serialize(stream, this); 
     stream.Seek(0, SeekOrigin.Begin); 
     clonedObject = formatter.Deserialize(stream); 
    } 

    return clonedObject; 
} 

Но быть в курсе, что это может вызвать проблемы с так называемым агрегатным объектом.

7

Если ваш класс только что свойства, вы могли бы сделать что-то вроде этого:

SubCentreMessage actual; 
actual = target.FindSubCentreFullDetails(120); //for Albany 
SubCentreMessage s = new SubCentreMessage(); 

//initialising s with the same values as 
foreach (var property in actual.GetType().GetProperties()) 
{ 
    PropertyInfo propertyS = s.GetType().GetProperty(property.Name); 
    var value = property.GetValue(actual, null); 
    propertyS.SetValue(s, property.GetValue(actual, null), null); 
} 

Если у вас есть поля и методы, я уверен, что вы можете восстановить их в новых отражений класса с использованием.Надеюсь, что это помогает

1

Как о чем-то вроде:

public class MyClass 
{ 
    int i; 
    double d; 
    string something; 

    public MyClass(int i, double d) {} 

    public MyClass Clone() 
    { 
     return (new MyClass(i, d)); 
    } 
} 

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

public MyClass CloneMore() 
    { 
     MyClass obj = new MyClass(i, d); 
     obj.something = this.something; 
     return (obj); 
    } 

Вызов с помощью:

MyClass us = them.Clone(); 

~~~

+0

Я думаю, что я написал класс, который расширяет объект с именем memberwiseclone, который использует отражение для получения/установки всех полей/свойств. – 2011-11-04 21:18:04

3

Как насчет JSON?

Вы можете получить JSON пакет из: https://www.nuget.org/packages/Newtonsoft.Json/

using Newtonsoft.Json; 
public static List<T> CopyAll<T>(this List<T> list) { 
    List<T> ret = new List<T>(); 
    string tmpStr = JsonConvert.SerializeObject(list); 
    ret = JsonConvert.DeserializeObject<List<T>>(tmpStr); 
    return ret; 
} 
+0

Спасибо @ saud-khan, это помогает мне достичь того, что я искал. – MatVAD

+0

это ... очень необходимо ... –

1

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

using System.Reflection; using System.Text.StringBuilder(); 

public static T CopyClass2<T>(T obj){ 
    T objcpy = (T)Activator.CreateInstance(typeof(T)); 
    obj.GetType().GetProperties().ToList() 
    .ForEach(p => objcpy.GetType().GetProperty(p.Name).SetValue(objcpy, p.GetValue(obj))); 
    return objcpy; 

}

А вот более подробная версия, я полагаю, из вышесказанного вы понимаете Лямбда-выражения, которые не распространены. Если вы уважаете читаемость (который является полностью действительным) больше, я хотел бы использовать ниже

public static T CopyClass<T>(T obj) 
{ 
    T objcpy = (T)Activator.CreateInstance(typeof(T)); 
    foreach (var prop in obj.GetType().GetProperties()) 
    { 
     var value = prop.GetValue(obj); 
     objcpy.GetType().GetProperty(prop.Name).SetValue(objcpy, value); 
    } 
    return objcpy; 

}

Используйте этот метод, чтобы перечислить имущество, чтобы увидеть, если он копируется в новые ссылки .. просто не перечислить свойства из всех примитивов (структурированных типов) .., поэтому вы получите название класса

public static string ListAllProperties<T>(T obj) 
{ 
     StringBuilder sb = new System.Text.StringBuilder(); 
     PropertyInfo[] propInfo = obj.GetType().GetProperties(); 
     foreach (var prop in propInfo) 
     { 
      var value = prop.GetValue(obj) ?? "(null)"; 
      sb.AppendLine(prop.Name + ": " + value.ToString()); 
     } 
     return sb.ToString(); 
} 
Смежные вопросы