C# ICloneable 얕은 복사(Shallow Copy), 깊은 복사(Deep Copy)

2021. 6. 16. 13:56기술/C#

반응형

Undo 기능을 구현할 때, 기존 자료구조를 복사해서 들고 있어야 한다.

기존 자료구조는 Dictionary로 구현이 돼있는데 이를 Deep Copy하는 방법을 알아보자.

 

Dictionay<uint, Node> listNode = new Dictionary<uint, Node>();

 

스택 자료구조로 구현해서 위에서 빼도 되는데 최대 N개만 들고 있기를 구현하려고 List 자료구조 사용

List<Dictionary<uint, Node>> stackNode = new List<Dictionary<uint, Node>>();

 

생성자를 사용해서 listNode를 복사하려고 했으나 이렇게 하면 참조값이 복사가 된다.

그러므로 기존 값이 변경됨에 따라 새로 생성한 자료들도 값이 변경되게 된다.

m_listUndo.Add( new Dictionary<uint, Node>(listNode) );

 

그래서 ICloneable을 사용하는데 값 복사가 필요한 Node 클래스에 인터페이스를 상속하고 구현한다.

class Node : ICloneable

{

   public object Clone()

   {

       // 값을 하나씩 대입해주어도 되지만 변수가 많을 경우를 대비해

       Node node = MemberwiseClone() as Node;

       return node;

   }

}

 

유저가 인풋 값이 있을 때 특정 부분에서 RecodeUndo() 함수를 실행한다.

void RecodeUndo()

{

   var cloneNode = new Dictionary<uint, Node>();

   cloneNode = m_mapNode.ToDictionary( node => node.Key, node => (Node)node.Value.Clone());

  m_listUndo.Add(cloneNode);

}

 

자 그런데 이 Node 클래스가 Tile 클래스를 참조하고 있다.

그럼 Tile 클래스도 함께 Clone()해주어야 한다.

 

class Tile : IConeable

{

   public object Clone()

   {

      return MemberwiseClone() as Tile;

   }

}

 

class Node : ICloneable

{

   public object Clone()

   {

       // 값을 하나씩 대입해주어도 되지만 변수가 많을 경우를 대비해

       Node node = MemberwiseClone() as Node;

       if (m_Tile != null)

          node.tile = m_tile.Clone() as Tile;

       return node;

   }

}

 

Undo() 기능을 실행할 때는 List의 뒤에서 부터 빼고 특정 N개가 넘어가면 앞에 자료들도 빼주어야 한다.

그렇게 들고 있는 자료들을 특정 N개로 맞춘다.

 

// RecodeUndo() 함수를 수정 : 유저가 불필요한 행동을 했을 때는 undo에 기록하지 않게 하기 위해서

void UserAction()

{

   // 행동 후에 값이 변경되니 미리 만들어 둔다

   bool undo = false;
   var cloneNode = new Dictionary<uint, Node>();
   cloneNode = m_Node.ToDictionary(node => node.Key, node => (Node)node.Value.Clone());

 

   // 유저 행동

   // ...

 

   // undo해야할 행동을 하였는가?

   if (undo)

   {

      RecodeUndo(cloneNode); // 기록한다

      if (undoable) // UI 표시 갱신

         panelUI.CheckUndoUI();

   }

}

 

void RecodeUndo(Dictionary<uint, Node> cloneNode)
{
   // 연속으로 최대 N번까지 되돌리기 가능
   if (m_listUndo.Count >= N)
      m_listUndo.RemoveAt(0); // 리스트의 앞을 빼준다.
            
      m_listUndo.Add(cloneMapNode);
}

반응형

'기술 > C#' 카테고리의 다른 글

박싱과 언박싱 struct와 class의 차이점에 대해서  (0) 2021.08.06