Дълбоко копие на обект означава копирания обект и копието да нямат еднаква референция, както и всички техни полета (от референтен тип).
Стандартния подход за създаване на дълбоко копие е обхождане на стойностите на свяко поле на обекта и “наливането” им в нови обекти от типа на полетата, който в последствие се “наливат” в копието. От гледна точка на бързодействие това е по-правилния вариант, според мен. Въпреки това при по-сложни ООП проекти този подход се оказва доста трудоемък. Доста по бърз за реализация е варианта с сриализиране на желания обект и в последствие неговата десериализация, при която върнатия обект просто се присвоява на копието.
Има и няколко условия за да може това да се реализира. Всеки клас, който ние сме дефинирали и се използва като поле в клонирания клас, трябва изрично да се означи като “сриализуем”, чрез атрибута [Serializable] над класа. Ако ще се сериализират циклични данни, трябва да се използва двоично сериализиране, понеже XML поради дървовидната си структура не може да се използва за сериализиране на обкети с циклична структура.
Време е за пример. В него ще използваме обект от тип граф с поле възел. Като класа възел има поле стойност и поле списък от наследници(също възли). За да направим дълбоко копие по обикновенния начин, трябва да обходим с DFS или BFS и да създаваме нови възли със същите стойности, като тези на оригинала. Подхода чрез сериализация е далеч по лесен за реализация, както ще видите по-надоло.
Това е нашия граф:
-
[Serializable]
-
public class Node<T>
-
{
-
public T value;
-
public List<Node<T>> descendantNodes;
-
public Node(T inValue)
-
{
-
value = inValue;
-
}
-
public Node()
-
{
-
}
-
}
-
[Serializable]
-
public class Graph<T>
-
{
-
public Node<T> root;
-
public Graph(Node<T> inRoot)
-
{
-
root = inRoot;
-
}
-
public Graph()
-
{
-
}
-
public override string ToString()
-
{
-
queue.Enqueue(root);
-
while (queue.Count != 0)
-
{
-
Node<T> currentNode = queue.Dequeue();
-
sb.Append(currentNode.value.ToString()+” “);
-
foreach (Node<T> item in currentNode.descendantNodes)
-
{
-
queue.Enqueue(item);
-
}
-
}
-
return sb.ToString();
-
}
-
}
Ето и метода, който ще използваме, за да създадем дълбоко копие на нашия граф:
-
public static object DeepCopy(object sourceObject)
-
{
-
formatter.Serialize(memStream, sourceObject);
-
memStream.Position = 0;
-
object resultObject = formatter.Deserialize(memStream);
-
return resultObject;
-
}
Ето и нашите тестове, за да видим наяве ползата от нашата функция:
-
static void Main(string[] args)
-
{
-
Graph<int> graph = CreateGraph();
-
Console.WriteLine(graph);
-
Console.WriteLine();
-
Graph<int> copyWithSameReferance = graph;
-
Console.WriteLine(“Originalniq graph: {0}”, graph);
-
Console.WriteLine(“Kopie na grafa no s ednakva referenciq: {0}”,copyWithSameReferance);
-
Console.WriteLine(“object.ReferenceEquals(graph,copyWithSameReferance) = {0}”, object.ReferenceEquals(graph, copyWithSameReferance));
-
Console.WriteLine();
-
Console.WriteLine(“Originalniq graph: {0}”, graph);
-
Console.WriteLine(“Kopie na grafa no s ednakva referenciq na poleto: {0}”, copyWithSameFiledRef);
-
Console.WriteLine(“object.ReferenceEquals(graph,copyWithSameFiledRef) = {0}”, object.ReferenceEquals(graph, copyWithSameFiledRef));
-
Console.WriteLine(“object.ReferenceEquals(graph.root, copyWithSameFiledRef.root) = {0}”, object.ReferenceEquals(graph.root, copyWithSameFiledRef.root));
-
Console.WriteLine();
-
Graph<int> deepCopyGraph = (Graph<int>)DeepCopy(graph);
-
Console.WriteLine(“Originalniq graph: {0}”, graph);
-
Console.WriteLine(“Dulboko kopie na grafa: {0}”, deepCopyGraph);
-
Console.WriteLine(“object.ReferenceEquals(graph,deepCopyGraph) = {0}”, object.ReferenceEquals(graph, deepCopyGraph));
-
Console.WriteLine(“object.ReferenceEquals(graph.root, deepCopyGraph.root) = {0}”, object.ReferenceEquals(graph.root, deepCopyGraph.root));
-
Console.WriteLine();
-
}
Тук е изхода от изпълнението на нашия пример:
Прикачам и целия проект на примера. Надявам се че съм Ви бил полезен 🙂