유니티 C# : C#의 깊은 복사 동작


C++ 에서도 얕은 복사와 깊은 복사 동작이 있었다.

기본 복사 생성자에 의해 얕은 복사가 진행되어

깊은 복사 동작은 우리가 직접 정의해 줬어야했다.




C#도 클래스의 기본 복사동작은 얕은 복사를 하고 있는데

깊은 복사를 정의하는 과정과 사용방법이 C++과

다른 점이 있어 포스팅을 하게 되었다.




우선 C#은 값 형식과 참조 형식으로 나누어지는데

클래스의 인스턴스는 태생부터 참조 형식이다.

때문에 단순히 대입을 하여 복사를 할 경우,

힙에 있는 인스턴스가 복사가 되는 것이 아니라

스택에 이 인스턴스를 가리키는 놈이 하나 더 생성된다.

(요기 까지는 C++의 문제점과 다를게 없다.)




자 그럼 간단하게 코드를 작성해보자.


class MyClass{


public int MyField1;

public int MyField2;


public MyClass DeepCopy(){


MyClass newCopy = new MyClass();

newCopy.MyField1 = this.MyField1;

newCopy.MyField2 = this.MyFiedl2;


return newCopy;

}

}


class Main{

static void main(string[] args) {


MyClass source = new MyClass();

source.MyField1 = 10;

source.MyField2 = 20;


MyClass target = source.DeepCopy();

target.MyField = 30;


}

}



이제 위의 예에서는 깊은 복사가 진행되어

source와 target이 힘에 서로 다른 객체를 가리킬 수 있다.

따라서 우리가 예상했던 결과가 나올 것이다.




또 하나 중요한 부분은 ICloneable.Clone() 메소드이다.

.NET 프레임워크의 System 네임스페이스에는 ICloneable

이라고 하는 인터페이스가 있다. 깊은 복사 기능을 가질

클래스가 .NET 프레임워크의 다른 유틸리티 클래스나

다른 프로그래머가 작성한 코드와 호환되도록 하고 싶다면

ICloneable을 상속하도록 하는 것이 좋다. ICloneable

인터페이스는 Clone() 메소드 하나만을 가지고 있다.




class MyClass : ICloneable{


public int MyField1;

public int MyField2;


public Object Clone(){

MyClass newCopy = new MyClass();

newCopy.MyField1 = this.MyField1;

newCopy.MyField2 = this.MyFiedl2;


return newCopy;

}

}



이제 이 클래스는 .NET 프레임워크와 호환성을 가지게 된다.

(Object로 반환 받을 수 있는 이유는 C#의 모든 데이터 형식은

Object를 상속받기 때문 : 전 포스팅 박싱/언박싱 참조)



C++ 디자인 패턴 : 전략 패턴 (strategy pattern)

함수 포인터로 구현한 전략 패턴




가상 함수를 대신할 방법을 찾아보자는 이슈에서

시작하여 전략패턴을 구현하는 방법까지 알아보게 되었다.

이미 NVI 관용구라는 훌륭한 방법이 있지만 좀 더 알아보겠다.




지금 우리는 게임의 캐릭터 클래스를 디자인 하고 있고

체력치를 계산하는 작업을 가상 함수를 통해 하고 있다.

체력을 계산하는 알고리즘은 캐릭터마다 다를 것이기 때문에

지극히 정상적인 설계이다. 하지만 여기서 가상 함수 대신

함수 포인터를 사용하여 좀 더 유연한 설계를 해보도록 하자.




함수 포인터를 이용한 전략 패턴은 체력치 계산을 어떤

캐릭터의 일부로 두지 않는다는 것부터 시작한다.

캐릭터의 생성자에 체력치 계산용 함수 포인터를

넘기게 만들고, 이 함수를 호출해서 실제 계산을 수행한다.




함수포인터를 이용한 전략(Strategy) 패턴의

단순한 예를 보여주는 클래스이다.






같은 캐릭터 타입으로부터 만들어진 객체들도

체력치 계산 함수를 다르게 가질 수 있는 융통성이 생겼다.

또한 게임이 실행되는 도중에 특정 캐릭터에 대한

체력치 계산함수를 바꿀수도 있다.




하지만 이 전략 패턴은 체력치가 계산되는

대상 객체의 비공개 데이터는 이 함수로 접근할 수 없다.

public 인터페이스로 얻은 정보만을 사용할 수 있기 때문이다.

( 멤버 함수가 아니기 때문에)




따라서 프렌드 함수로 선언을 하여 비공개 데이터에

접근을 할 수 있게 하여 캡슐화를 떨어뜨릴 것이냐.

즉 , 캡슐화냐 캡슐화를 떨어뜨려 얻을 수 있는 전략 패턴의

이점이 중요한가는 실제로 맡은 설계를 보면서 판단해야 한다.




C# 기초 : 가변길이 매개 변수 , 명명된 매개 변수




C++를 하다가 넘어 왔는데, 다른 부분이 조금씩 보이기 시작

가변길이 매개 변수에 대해 먼저 알아보도록 하자.




1. 가변길이 매개 변수

함수를 오버로딩하는 과정에서

똑같은 타입인데 매개변수의 '수'가

다르다는 이유만으로 오버로딩을 했다.

C#은 가변길이 매개 변수를 제공하여

그 개수가 유연하게 변할 수 있게 한다.




int total = 0;


total = Sum( 1, 2, 3);

total = Sum( 1, 2, 3, 4, 5);

total = Sum( 1, 2, 3, 4, 5, 6, 7, 8);


위와 같이 매개 변수의 수가 정해져 있지 않다면

(똑같은 타입일 경우에만) 그 만큼 함수를 오버로딩해서

사용하는 것이 아니라 가변길이 매개 변수를 사용한다.




가변길이 매개 변수는 params 키워드와 배열을 이용해서 선언.


int Sum ( params int[] args ){


int sum = 0;


for( int i = 0; i<args.Length; i++){

sum += args[i];

}


return sum;

}


==> 형식은 같으나 매개 변수의 개수만 유연하게 달라질 경우에 적합하다.




2. 명명된 매개 변수


메소드를 호출할 때 매개 변수 목록 중 어느 매개 변수에 데이터를

할당할 것인지를 지정하는 것은 '순서'이다. 명명된 매개 변수는

순서가아닌 매개 변수의 이름에 근거해서 데이터를 할당한다.




static void Print( string name , int phone ){ ... }


static void Main(string[] args){

Print ( name : "홍길동" , phone : "2222" );

Print ("김사랑" , "11111" );

Print ( phone : "3333" , name : "한고은" );

}




다소 번거로워 보일 수도 있지만, 매개 변수의 수가 많은 경우

가독성을 높여줄 수 있다. 또한 디폴드 매개 변수가 많은 경우에도

가독성이 상당히 안좋아질 수가 있는데, 명명된 매개 변수를

사용할 경우 이 문제를 해결할 수 있다.









+ Recent posts