Moje dwa centy na Deep copy vs Shallow copy w .Net

Microsoft .Net zapewnia obsługę klonowania obiektów - możliwość tworzenia dokładnej kopii obiektu (znanej również jako klon). Klonowanie może być dwojakiego rodzaju: kopiowanie płytkie i kopiowanie głębokie. Podczas gdy pierwszą można zaimplementować, wywołując metodę MemberwiseClone klasy System.Object, implementacja drugiej jest nieco skomplikowana, ponieważ domyślnie nie jest ona obsługiwana w ramach. W istocie, podczas gdy płytka kopia kopiuje odniesienia bez obiektów, do których są odniesienia, głęboki klon tworzy kopię obiektu źródłowego wraz z jego odniesieniami.

Jakie są wszystkie dostępne opcje klonowania?

Aby sklonować wystąpienie klasy w języku C #, masz do wyboru kilka opcji. Należą do nich:

  • Użycie metody System.Object.MemberwiseClone do wykonania płytkiej kopii
  • Korzystanie z Reflection, korzystając z metody Activator.CreateInstance
  • Korzystanie z serializacji
  • Poprzez implementację interfejsu IClonable

Należy pamiętać, że podczas klonowania obiektów lub instancji klas w .Net nie trzeba brać pod uwagę statycznych elementów członkowskich ani pól statycznych. Powodem jest to, że obiekty statyczne są przechowywane we współdzielonej lokalizacji pamięci i masz przydzieloną jedną lokalizację pamięci na domenę aplikacji.

Płytka kopia a głęboka kopia

Rozważmy klasę Employee i że tworzymy instancję klasy Employee, jak pokazano poniżej.

Employee emp = new Employee();

Employee clone = emp;

Zapoznaj się z powyższym fragmentem kodu. Operator przypisania „=” skopiowałby odniesienie, a nie rzeczywisty obiekt. Metoda MemberwiseClone () zdefiniowana w klasie System.Object robi dokładnie to samo. To są przykłady płytkiej kopii. Stąd, kiedy używasz operatora przypisania do kopiowania i obiektowania do innego lub, używając metody Memberwise.Clone (), faktycznie robisz płytką kopię obiektu.

Podczas gdy w płytkiej kopii elementy składowe skopiowanego obiektu odwołują się do tego samego obiektu, co obiekt oryginalny, w głębokiej kopii oddzielne wystąpienia każdego elementu członkowskiego typu referencyjnego w pierwotnej instancji są tworzone w nowej lub sklonowanej instancji. Dlatego jeśli masz typ referencyjny w oryginalnej instancji, nowa instancja będzie również zawierała ten sam element członkowski typu referencyjnego, ale ten typ referencyjny będzie wskazywał na zupełnie nową instancję.

W płytkiej kopii tworzony jest nowy obiekt, a następnie niestatyczne elementy składowe obiektu źródłowego są kopiowane do obiektu docelowego lub nowego obiektu. Jeśli element członkowski jest polem typu wartości, to wykonywana jest bitowa kopia pola. Natomiast jeśli kopiowany element członkowski jest typem referencyjnym, odwołanie jest kopiowane. W związku z tym element referencyjny wewnątrz oryginalnego obiektu i obiekty docelowe odwołują się do tego samego obiektu w pamięci.

Jeśli masz kolekcję z pojedynczymi elementami w środku i chcesz wykonać płytką kopię instancji kolekcji. Należy zauważyć, że płytka kopia instancji kolekcji kopiuje strukturę kolekcji, ale nie elementy wewnątrz kolekcji. Dlatego po wykonaniu płytkiej kopii wystąpienia kolekcji powstałyby dwie kolekcje współużytkujące poszczególne elementy kolekcji. Wręcz przeciwnie, jeśli wykonasz głęboką kopię instancji kolekcji, będziesz miał dwie instancje kolekcji z powielonymi pojedynczymi elementami oryginalnej kolekcji.

Wdrażanie głębokiego kopiowania przy użyciu serializacji

Możesz wdrożyć głębokie kopiowanie na wiele sposobów. Jednym z najbardziej preferowanych sposobów implementacji głębokiej kopii obiektu jest użycie serializacji. Możesz także wykorzystać odbicie, aby wykonać głęboką kopię instancji klasy. Poniższy fragment kodu ilustruje, jak można napisać metodę, która implementuje serializację binarną, aby wykonać głęboką kopię wystąpienia przy użyciu C #.

public static T DeepCopy(T obj)

       {

           if (!typeof(T).IsSerializable)

           {

               throw new Exception("The source object must be serializable");

           }

           if (Object.ReferenceEquals(obj, null))

           {

               throw new Exception("The source object must not be null");

           }

           T result = default(T);

           using (var memoryStream = new MemoryStream())

           {

                var formatter = new BinaryFormatter();

               formatter.Serialize(memoryStream, obj);

               memoryStream.Seek(0, SeekOrigin.Begin);

               result = (T)formatter.Deserialize(memoryStream);

               memoryStream.Close();

           }

           return result;

       }

Biorąc pod uwagę, że masz klasę encji o nazwie Employee, możesz wykonać głęboką kopię wystąpienia klasy Employee, jak pokazano w poniższym fragmencie kodu.

static void Main(string[] args)

       {

           Employee emp = new Employee();

           emp.EmployeeId = 1;

           emp.FirstName = "Joydip";

           emp.LastName = "Kanjilal";

           Employee clone = DeepCopy(emp);

           if(Object.ReferenceEquals(emp, clone))

           {

               Console.WriteLine("References are the same.");

           }

           else

           {

               Console.WriteLine("References are different.");

           }

       }

Po uruchomieniu powyższego programu zostanie wykonana głęboka kopia instancji „emp” i komunikat „References are different”. zostanie wyświetlone.