ice rabbit programming

[C#] Delegate 본문

Development/C#

[C#] Delegate

판교토끼 2020. 9. 5. 23:50

잡담 글에서 한 번 언급했던 것처럼, 개발 스택이 C# 클라이언트 프로그래밍에서 Python 서버 프로그래밍으로 넘어가면서 C#은 6월 말 이후로 다루지 않고 있다. 사실상 작년까지는 C# 이름만 알았지 다루어본적도 없어서 이번에 반 년 정도 다룬 걸로는 뭔가 배웠다고도 하기 쉽지 않지만, 기왕 수업도 듣고 현업 업무도 조금이나마 해보았으니 정리해둔 내용은 마저 정리하려고 한다. (C# 포스팅을 하지 않은 사이에 티스토리 코드블럭 Hylighting에 C#이 생겼다!)

이전에 Generic까지 했는데, 이번 글에서는 Delegate를 보려고 한다.

Delegate의 기초 개념

Delegate는 쉽게 말해 메소드(함수)의 호출 정보/시그니쳐/주소를 저장하는 타입이다. C++에서의 함수 포인터 개념과 비슷하다고 생각할 수 있겠다. 일단 코드로 한 번 보자.

delegate void FUNC(int arg); // FUNC void int arg delegate type.
// ...
FUNC f = foo;
f(10); // foo(10)과 같음
f.Invoke(10); // foo(10) 실행과 같음

 

이런 식으로 함수의 return 값, 인자 등 시그니쳐를 지정해두고 그렇게 생긴 함수를 담는다. 위 코드는 실제로는 아래와 같이 동작한다.

delegate void FUNC(int arg);
// ...
class FUNC : System.MulticastDelegate {
	//...
}

// 아래 코드들은 같은 foo 함수를 담게 된다.
FUNC f1 = new FUNC(foo);
FUNC f2 = foo;
FUNC f3 = delegate(int a, int b) { return a+b };
f2(10); // f2.Invoke(10)

 

공통성과 가변성 분리(Delegate 사용 예)

delegate의 개념은 위에서 다루었는데, 실제로 어떤 식으로 사용하는 것인지 보자. 공통성과 가변성의 분리에서 주로 사용하는데, 함수, 상속은 주로 공통으로 수행되는 부분을 모듈화할 때 사용하게 된다. 이 때 대부분은 겹치는데 호출하는 함수가 변할 수 있다면? 함수를 다르게 적어주어야 하니 수행하는 로직은 동일하더라도 묶어낼 수 없다는 문제가 발생한다. 이 때 delegater를 사용하게 된다. 예시를 보자.

public static void Sort(int[] arr, Comparison cmp) { // Comparison : delegate type
	int sz = arr.GetLength(0);
    for(int i=0;i<sz-1;i++) {
    	for(int j=i+1;j<sz;j++) {
        	if(cmp(arr[i],arr[j]>0) // delegate
            	Swap(ref arr[i], ref arr[j]);
        }
    }
}

 

위 예시 코드에서 cmp는 delegate type이다. 즉 Comparison과 같은 시그니쳐를 가지는 함수가 가변적으로 들어올 수 있는 것이다. 그렇다면 위에서 발생한 공통성과 가변성의 분리 문제를 해결할 수 있다.

Delegate의 특징

  • delegate<T>와 같이 Generic을 적용할 수 있다.
  • Delegate는 reference type이고, immutable하다.
  • Delegate Chain이라는 결합(Combine)을 사용할 수 있다. 기존 delegate를 결합한 새로운 delegate 객체가 생긴다.
FUNC f1 = Test.Method1;
FUNC f2 = Test.Method2;

FUNC f4 = (FUNC)Delegate.Combine(f1,f2); // delegate type
FUNC f5 = f1+f2; // f4와 같다. +,-도 동작한다. +=도 쓸 수 있다.

 

Event Handler

Event 발생 시에 실행되는 handler에 delegate를 등록하는 방식으로 활용이 가능하다. 본인은 이것이 delegate인지 모르고 사용하고 있다가 수업을 수강하면서 알았다. 구독자(sub) 간의 독립성을 위해 public event Handler handler와 같이 event 키워드를 붙여준다. event 키워드를 붙일 경우 등록 시에 += 연산자만 사용이 가능하다(= 사용 불가).

// +=
private event HANDLER h; // event keyword
public void addHandler(HANDLER f1) {
	h += f1; // 이벤트가 발생하여 h가 실행되면 f1과 chain이 되어 함께 일어난다.
}

 

특수 Delegate 종류

뭐라고 붙일지 몰라서 특수라고 쓰긴 했는데, 용법이 다르거나 한 건 아니고 시그니쳐의 분류이다.

  • Action : return type이 없는 메소드를 담을 수 있는 delegate
  • Func : return type이 있는 메소드를 담을 수 있는 deleagte

Lambda

메소드명을 넣어야 하는 부ㅜㄴ에 구현체를 넣는 표현식으로, =>를 사용한다. 자바스크립트에서의 Arrow Function과 유사하게 보면 되겠다.

// 아래 f1~f4는 모두 같다.
Func<int,int,int> f1 = Add;
Func<int,int,int> f2 = (int a, int b) => {return a+b;};
Func<int,int,int> f3 = (a,b) => {return a+b;};
Func<int,int,int> f4 = (a,b) => a+b;

// 아래 f5,f6은 같다.
Func<int,int> f5 = (a) => a+a;
Func<int,int> f6 = a => a_a;

 

꽤나 오랜만에 C#을 다시 봤는데, 그래도 다시 보니 공부했던 내용들이 정리되는 기분이다. 이후 글들에서는 예외처리, Collection, CO-ROUTINE, LINQ, Threading, GC에 대해서 다룰 예정이다. 그러면 Basic 과정에서 수강한 부분은 얼추 마무리될 것 같다.

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

[C#] 예외 처리(Exception Handling)  (2) 2021.03.28
[C#] Generic  (0) 2020.05.24
[C#] Index와 Range  (0) 2020.05.17
[C#] Property와 Indexer  (0) 2020.05.10
[C#] 메소드  (0) 2020.05.04