728x90

직렬화(Serialization)

 

정의(definition)

직렬화(Serialization)는 메모리 내부의 오브젝트(object)나 오브젝트 그래프(object graph; 서로를 참조하는 오브젝트들의 집합)을 바이트(bytes)로 이루어진 스트림(stream)이나 XML 노드들로 만들어 보관되거나 전송될 수 있도록 변환하는 것을 말한다. 

역직렬화(Deserialization)는 반대로 데이터 스트림을 메모리 내부의 오브젝트나 오브젝트 그래프로 재구성하는 것을 말한다.

 

목적(purpose)

직렬화와 역직렬화는 주로 다음 두 목적으로 사용된다.

오브젝트를 네트워크나 어플리케이션 경계 상으로 전송하는 목적.

파일이나 데이터베이스로 오브젝트의 표현(representation)을 보관하는 목적. 

 

유니티에서의 직렬화

유니티(Unity)에서의 직렬화는 객체의 정보 은폐(Information Hiding)를 해치지 않으면서 

Inspector창을 통해 해당 변수를 입력 받는 용도로 사용한다.

728x90
Posted by 정망스
,
728x90

재귀 호출은 for문 while문 같은 반복에 비해 더 짧게 구현 가능한 경우가 많기 때문에 함수형 접근 방식에 적합하다고 한다. 대신 설계 및 테스트는 어려운 편


재귀 호출은 기본 케이스를 갖는 것이 일반적이며 기본 케이스란 재귀의 종료 조건을 정의하는 것을 말한다. 


반복하는 형태의 구현이 더 효율적인 경우에 컴파일러가 재귀 호출을 반복으로 변환하기도 한다고 한다.


기본 케이스가 실행될 때까지 끊임없이 자신을 호출하는 형태직접 재귀 호출(꼬리 재귀, 누적기 전달형, 연속 전달형..)이라 하고, 

간접 재귀 호출이라는 것도 있는데 간접 재귀는 최소 두 개의 함수가 관여하는 형태로 함수 A,B가 존재할 경우 함수 A가 B를 호출하고, 함수 B가 다시 A를 호출하는 형태

이 형태를 재귀 호출로 볼수 있는 근거는 함수 B가 A를 호출할 때 함수 A는 이미 B를 호출하면서 활성화 되었기 때문. 즉 함수 B가 A를 호출했을 때, 함수 A의 호출이 아직 완료되지 않은 상태로 남아 있다.

bool IsOdd(int number)

{

if (number == 0) return false;

else return IsEven(number - 1);

}

bool IsEven(int number)

{

if (number == 0) return true;

else return IsOdd(number - 1);

}


LINQ의 Aggregate를 적용하면 재귀 함수를 함수형 접근 방식으로 리펙토링 할수 있다.

int[] numbers = { 1, 2, 3, 4, 5 };

var data = numbers.Aggregate((num1, num2) => num1 * num2);

결과 : 120


요약하면 재귀 함수를 사용하면, 표현이 어렵지 않게 누구나 보기에 자연? 스러워 진다면 가독성이 좋아지고, 변수의 사용을 줄여줌으로써 함수형 프로그래밍에 알맞다라고 하는거 같다.

함수를 일급 객체로 취급하는 함수형 프로그래밍에서 함수에서 함수를 호출하여 처리 하는 방식인 점도 있는거 같다.

하지만 재귀 함수도 잘못 사용 되어질 경우엔 메모리를 많이 차지할수도 있고 성능이 안좋을수도 있다 (스택에 매개변수, 지역변수, 리턴 값, 종료후 돌아갈 위치 등 스택 메모리에 쌓이니까.. 스택오버플로우)

그리고 가독성이 좋아진다라고 책에는 정리 되어 있지만, 복잡한 로직의 재귀 함수일 경우엔 오히려 이해하거나 보는데 있어서 가독성을 해칠 경우가 있을거 같다.

즉, 재귀 함수의 형태는 함수형 프로그래밍에 맞지만 이것을 이용하여 개발하는데 있어서는 주의할 점과, 고려해야할 것 등 어려움이 많을수 있다 정도 인거 같다.

728x90
Posted by 정망스
,
728x90


유니티에서 비동기? 처럼 프로그래밍을 하는 대표적인 방법으로는 코루틴을 이용한 방식이라고 생각하고 있다.

굳이 단점이라면 예외처리가 안되고, 값 리턴이 불가능 하다는것


Async와, Await를 이용함으로써 비동기 프로그래밍을 작성할때

예외처리가 가능하고, 값 리턴이 가능하다는것


하지만 유니티로 개발을 하면서 과연 Async, Await 등을 이용하여 비동기 프로그래밍을 작성할 상황이

얼마나 많을까? 하는 의문이 들기는 하다.


monobehaviour를 상속받지 않는 클래스에서 비동기 방식이 필요하다면 사용 해볼법도 한 것 같다.


1. 코루틴

private IEnumerator WaitOneSecond()

{

        yield return new WaitForSeconds(1.0f);

        Debug.Log("Finished waiting.");

}


2. Async, Await

private async Task WaitOneSecondAsync()

{

        await Task.Delay(TimeSpan.FromSeconds(1));

        Debug.Log("Finished waiting.");

}


쓰임새는 다르지만 기능은 1초를 기다린후 로그를 출력하는 동일한 방식


C# 버전이 올라가면서 제공되는 비동기 프로그래밍의 기본 방법이 추가 된 것일 뿐

둘중에 어떤 것이 더 좋다라고 하기 보단, 상황에 따라 활용할 수 있는 방법이 추가 된 걸로 받아들였다.


728x90
Posted by 정망스
,
728x90

확장 메서드는 기존 클래스나 형식에 변경을 가하지 않고 기능을 확장할 수 있는 방법이다.


public static bool Extension(this string str) {...}

처럼 첫 번째 인수에 this 키워드가 있는데 이것이 확장 메서드임을 의미한다.

확장 메서드는 정적 클래스에서만 정의할 수 있다.


메서드 체인은 읽기 쉽고 짧은 코드를 작성할 수 있도록 해주기에 함수형 프로그래밍 작성에 어울리고, 메서드 체인은 확장 메서드에 의존하는 관계를 가진다.


A(), B() 함수를 이용한 string 값을 만드는 상황에서

확장메서드가 미적용일 경우엔

string result = "";

string strTemp = "ABCD";

strTemp = A(strTemp);

strTemp = B(strTemp);

result = strTemp;


확장메서드를 적용할 경우엔

string result = "";

string strTemp = "ABCD";

result = strTemp.A().B();

처럼 코드를 더 간결하게 만들수 있다 라는 장점이 있다.


기존에 정의되어 있는 메서드와 동일한 이름을 갖는 확장 메서드를 만들 경우 매개 변수 형식과 갯수까지 같다면 기존 메서드가 실행 되므로 주의 해야 하고,

기존 형식의 코드 변경 없이 개발자 임의로 만든 메서드를 대상 형식에 쉽게 추가를 할수 있지만 너무 과하게 사용한다면 혼란을 야기할 수도 있기 때문에 적당히 사용한다면 좋은 프로그래밍 작성에 도움이 될 듯 하다.



728x90
Posted by 정망스
,
728x90

 

대리자는 매개 변수와 반환 형식을 갖는 메서드를 캡슐화 하는 형식.

메서드의 참조를 저장한다는 점에서 C/C++의 함수 포인터와 비슷하다. 

대리자는 참조하는 메서드의 메모리 주소를 가진다.

대리자 형식으로 다음처럼 정의 된다.

[접근 한정자] delegate 반환형식 대리자이름(매개 변수);

public delegate void StudyDelegate(string studyString);

대리자는 아래처럼 같은 형식을 갖는 메서드를 할당 하여 사용 할 수 있다.

public delegate void StudyDelegate(string studyString);

public static void TestDelegate(string TestString)
{
	Console.WriteLine(dateText);
}

StudyDelegate delegate = TestDelegate;

 

하나의 특정 메서드를 대리자 변수에 할당하는 형태의 대리자를 유니캐스트 대리자 라고 부른다.

대리자는 한 개의 대리자 변수를 통해 여러 개의 메서드를 호출할 수 있는데 이런 형태의 대리자를

멀티캐스트 대리자라 부른다.

1. Delegate.Combine(), Delegate.Remove() 메서드를 이용한 멀티캐스트 대리자 사용

private delegate void StudyDelegate(int a);

private state void A(int x) {...};
private state void B(int x) {...};
private state void C(int x) {...};
private state void D(int x) {...};

StudyDelegate aDel = A;
StudyDelegate bDel = B;
StudyDelegate cDel = C;
StudyDelegate dDel = D;

StudyDelegate studydelegate = (StudyDelegate)Delegate.Combine(new StudyDelegate[] {A, B, C, D});

StudyDelegate studydelegate2 = (StudyDelegate)Delegate.Combine(A, B, C, D);

StudyDelegate studydelegate3 = (StudyDelegate)Delegate.Remove(studydelegate, C);

2. +=, -= 연산자를 이용한 멀티캐스트 대리자 사용

private delegate void StudyDelegate(int a);

private state void A(int x) {...};
private state void B(int x) {...};
private state void C(int x) {...};
private state void D(int x) {...};

StudyDelegate aDel = A;
StudyDelegate bDel = B;
StudyDelegate cDel = C;
StudyDelegate dDel = D;

StudyDelegate studydelegate = aDel + bDel;
studydelegate += cDel;
studydelegate += dDel;

studydelegate = studydelegate - dDel;
studydelegate -= cDel;

+=, -=을 이용해 본래 멀티캐스트 대리자에 할당하는 행위는 함수형 프로그래밍의 불변성 개념에 맞지 않다고 한다.

 

대리자는 제네릭 형식을 매개 변수로도 이용할 수 있다.

private delegate T StudyDelegate<T>(T a);

private static int intDel (int x) {...};
private static double doubleDel (double x) {...};

StudyDelegate<int> intDelegate = intDel;
StudyDelegate<double> doubleDelegate = doubleDel;

제네릭 대리자 사용시 제네릭 매개 변수를 다중으로 사용 할 수 있다.

private delegate void StudyDelegate<T1, T2>(T1 a, T2 b);

private static void int_double_Del (int x, double y) {...};

StudyDelegate<int, double> multiDelegate = int_double_Del;

반환값을 갖는 다중 제네릭 대리자도 사용 할 수 있다.

private delegate TResult StudyDelegate<T1, T2, TResult>(T1 a, T2 b);

private static float int_double_Del (int x, double y) 
{
  float result = 1f;
  ...
  return result;
}

StudyDelegate<int, double, float> multiDelegate = int_double_Del;

C# 에서는 void 반환 형식의 내장 대리자인 Action과, 반환 형식을 갖는 Func 내장 대리자를 제공한다.

제공되는 대리자 이름을 대신 사용할 뿐 앞에서 본 반환값이 없는, 그리고 있는 제네릭 대리자와 사용법은 같다.

Action<int, double> delegate1 = int_double_del;
Func<int, double, float> delegate2 = int_double_float_del

 

제네릭 대리자를 이용하면 해당 대리자와 형식이 다른 메서드를 할당할 수 있는데

이것을 대리자에서의 가변성이라고 한다. 대리자의 가변성에는 공변성과 반공변성이 있다.

공변성 : 대리자에서 정의하고 있는 반환 형식보다 하위의 상속 형식 반환을 허용

반공변성 : 할당된 메서드가 대리자에서 정의하고 있는 매개 변수 형식보다 상위 형식의 매개 변수를 취할 수 있다

보고있는 책에서 정리된 내용인데.. 이해가 될려니 말려니.. 말이 너무 어렵다.

내가 이해하기 쉽도록 말을 정리 해보려 노력한다...

부모클래스는 자식을 받아들일수 있으나 자식클래스는 부모를 받아들일수 없다.

(쓰고 보니 참.. 조금 그런 말이다..)

 

기본적인 함수의 형태는 리턴타입 함수이름(인수)의 형태 인데, 여기서 리턴 타입과 인수가 대입이 일어나는 위치로 여기서 공변성반공변성이라는 성질이 발생하는데

선언한 델리게이트의 리턴타입이 부모클래스라면 
자식클래스 및 부모클래스 타입을 리턴 타입으로 가지는 모든 함수를 받아준다. 

리턴타입의 경우는 부모클래스와 자식클래스의 원칙을 그대로 따르므로 공변성을 가진다.

B (class)는 A (class)를 상속받고 있다라는 가정

delegate A aDelegate();

static B bMethod()
{
  B b = null;
  ...
  return b;
}

aDelegate aDel = bMethod;
B b = (B)aDel();

 

하지만 인수는 리턴타입과 반대로 동작하는 반공변성을 가진다.

인수는 델리게이트 객체에 함수를 대입할 때가 아니라 함수를 호출 할 때 대입이 일어나므로 비교 시점이 호출할 때 이다

그래서 호출할 때 델리게이트에 등록되어있는 인수의 타입보다 더 큰, 상위 개념의 인수를 호출할 수도 있는, 공변성과는 반대 되는 (반공변성)이 적용된다.

B (class)는 A (class)를 상속받고 있다라는 가정

delegate void bDelegate(B b);

static void aMethod(A a) {...}

bDelegate bDel = aMethod;
A a = null;

bDel((B)a);
728x90
Posted by 정망스
,


맨 위로
홈으로 ▲위로 ▼아래로 ♥댓글쓰기 새로고침