'action'에 해당되는 글 1건

  1. 2021.07.23 C# - 대리자 (delegate) (함수형 프로그래밍)
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 정망스
,


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