[C#] Delegate

jiho·2020년 1월 9일
0

C#

목록 보기
2/4

method를 실행시키는 또 다른 방법은 delegate를 사용하는 것이다. 만약 당신이 function pointers를 지원하는 언어는 사용해왔다면(함수형 프로그래밍을 해봤다면) delegate를
type safe method pointer로 생각하면 됩니다!
다시말해서 delegate는 정확한 parameter 타입들을 가지고 안전하게 호출되기위해서 delegate와 같은 signature로 매칭되는 method의 메모리 주소를 가지고 있습니다.

예를 들어, Person class 속에 string 타입을 가져야하고 int 타입을 리턴하는 함수가 있다고 가정합시다.

public int MethodIWantToCall(string input){
	return input.Length;
}

저희는 Person의 instance를 통해 호출할 수 있습니다.

int answer = p1.MethodIWantToCall("Frog");

대안으로 저희는 간접적으로 메소드를 호출하기위해 matching signature를 가진 delegate를 아래 코드처럼 정의할 수 있습니다. paramter의 이름은 매칭되지 않아도 됩니다. 오직 parameter의 타입과 리턴 값의 타입만 매칭되야합니다.

delegate int DelegateWithMatchingSignatrue(string s);

그리고 저희는 delegate의 instance를 만들 수 있습니다. 그리고 마침내 delegate를 호출할 수 있습니다.(함수를 호출할 수 있습니다.)

var d = new DelegateWithMatchingSignature(p1.MethodIWantToCall);
int answer2 = d("Frog");
// or
int answer3 = d.invoke("Frog"); 

여기서 이해를 위해서 개인적인 설명을 붙이자면 d는 특정한 형태의 함수를 담을 수 있는 변수(delegate)를 생성한 것입니다. 해당 변수를 통해 담아져있는 함수를 위와 같이 호출 또한 가능한 것이고요.

그래서 요점은 delegate를 유연함을 제공합니다.
예를들어 우리는 순서대로 호출될 필요가 있는 method들의 큐를 만들기 위해 delegate들을 만들 수 있었습니다. 수행될 필요가 있는 actions들을 Queuing하는 것은 확장성이 필요한 서비스에서 흔합니다.

또 다른 예는 병렬적으로 수행되기 위해서 다중 actions을 수행하는 것입니다. Delegate들은 다른 thread에서 실행되는 비동기 연산을 위해 지원해왔습니다. 그리고 더 나은 반응성을 가질 수 있었습니다. 이 부분은 추후에 동시성 프로그래밍을 할 때 다시 소개해보겠습니다.

가장 중요한 예는 delegate는 우리가 서로에 대해 알 필요없는 다른 객체 사이에서 message를 보내기위해 event들을 구현할 수 있게 합니다.

Delegates와 events는 C#에서 가장 혼란스러운 기능중 두 개 입니다.

Event Handler with Delegate

간단히 이벤트 처리를 위한 이벤트 핸들러로 Delegate를 사용해보겠습니다.
Microsoft에서는 이벤트를 사용하기 위해 미리 정의된 delegate 두 개가 있습니다. 해당 signature은 간단합니다.

public delegate void EventHandler(object sender, EventArgs e);
// or
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

만약에 우리가 정의한 이벤트를 사용하고 싶을 때는 두번 째 delegate를 사용하면 됩니다.
네임스페이스까지 모두 쓰면 다음같습니다. System.EventHandler

using System;
namespace Test{
  public class Person {
      public EventHandler Shout;
      public int AngerLevel;
      public void Poke(){
        AngerLevel++;
        if (AngerLevel >= 3){
              Shout?.Invoke(this, EventArgs.Empty);
        }
      }
  }

  class Program {
      static void main(string[] args){
          var harry = new Person();
          void Harry_Shout (object sender, EventArgs e){
              Person p = (Person)sender;
              Console.WriteLine($"{p.Name} is this angry: {p.AngerLevel}");
          }
          harry.Shout = Harry_Shout;
          harry.Poke()
          harry.Poke()
          harry.Poke()
          harry.Poke()
      }
  }
}

System Lib에 있는 표준 eventhandler를 통해 delegate를 살펴봤습니다.
delegate에 여러개의 method를 넣을 수 있습니다. 그 때는 =(assignment)대신 += 연산을 통해 넣어 주면됩니다.

기존에 python 이나 javascript처럼 콜백함수를 넣어주기만하면됩니다. 대신 함수의 signature가 정해져있을 뿐입니다. 그 부분만 지켜주면 사용하는 부분은 동일합니다.

추가적으로 event keyword에 대해 설명하겠습니다.
위 코드에서 harry의 Shout delegate에 Harry_Shout method를 추가해주는 부분에 =을 사용할 경우, 기존에 delegate에 있던 함수들이 모두 제거대고 Harry_Shout함수로 교체됩니다. 이러한 이벤트를 위해 사용되는 Delegate를 사용할 때, += or -=만들 사용할 것을 주의를 줍니다.
이것을 강제하도록 하는 키워드가 존재합니다.

event라는 키워드입니다.

그래서 Person에 Shout Field를 정의하는 부분에 아래와 같이 선언해주면 Shout에 할당은 오직 += 로만 할 수 있습니다.

...
public class Person {
      public event EventHandler Shout;
...
profile
Scratch, Under the hood, Initial version analysis

0개의 댓글