C#/C# 기본

[C#] 대리자와 이벤트

로파이 2021. 10. 15. 12:43

대리자 delegate

대리자란 함수의 호출을 대신하는 객체로 C++의 function 혹은 임의의 함수를 타입지정하는 것과 유사하다.

 

한정자 delegate 반환 형식 대리자_이름(매개변수_목록)

 delegate int MyDelegate(int a, int b);

MyDelegate 타입은 두 int 형 매개변수를 받아 int를 반환하는 함수를 의미한다.

 

대리자를 통한 함수 호출은 다음과 같다.

1) 대리자를 선언한다.

2) 대리자 인스턴스를 만들고 함수를 등록한다.

3) 대리자를 호출한다.

 

C++과 다르게 대리자의 함수의 경우 멤버 함수와 정적 함수 모두 형식만 맞으면 등록하여 호출할 수 있다는 점이다. C++에서 멤버 함수의 경우 첫번쨰 인자에 인스턴스를 바인딩해야한다.

    class Calculator
    {
        public int Plus(int a, int b)
        {
            return a + b;
        }
        public static int Minus(int a, int b)
        {
            return a - b;
        }
    }
static void Main(string[] args)
{
	Calculator Calc = new Calculator();
	MyDelegate Callback;

	Callback = new MyDelegate(Calc.Plus); // 멤버 함수의 등록
	Console.WriteLine(Callback(3, 4));

	Callback = new MyDelegate(Calculator.Minus); // 정적 멤버 함수의 등록
	Console.WriteLine(Callback(5, 3));
}

 

일반화 대리자

T로 일반화된 대리자를 사용할 수 있다. 다음 정렬 함수에서 두 수의 대소 비교를 하는 함수를 대리자로 사용할 수 있다.

        delegate int Comparer(int a, int b);
        static void BubbleSort(int[] DataSet, Comparer Pred)
        {
            for(int p = 0; p < DataSet.Length -1; ++p)
            {
                for(int q = 0; q < DataSet.Length - 1 - p; ++q)
                {
                    // 정렬을 위한 비교 함수의 대리자
                    if(Pred(DataSet[q], DataSet[q+1]) > 0)
                    {
                        int temp = DataSet[q+1];
                        DataSet[q+1] = DataSet[q];
                        DataSet[q] = temp;
                    }
                }
            }
        }

IComparable<T>를 상속하는 T 객체는 항상 CompareTo 메서드를 제공하기 때문에 다음과 같이 일반화 대리자를 서언하여 해당 정렬 함수에 비교 대리자로 등록할 수 있다.

    static int AscendCompare<T>(T a, T b) where T : IComparable<T>
	{
		return a.CompareTo(b);
	}
           int[] array = { 3, 4, 2, 1, 8, 6, 5 };

            Console.WriteLine("Sorting Asceneding...");

            BubbleSort(array, new Comparer(AscendCompare));

 

대리자 체인

대리자 인스턴스는 둘 이상의 콜백 함수를 등록할 수 있다. 대리자를 통해 함수를 호출하면 차례대로 등록된 함수가 호출된다.

        delegate void ThereIsAFire(string location);

        static void Call119(string location)
        {
            Console.WriteLine("소방서죠? 불났아어! 주소는 : {0}", location);
        }
        static void ShotOut(string location)
        {
            Console.WriteLine("피하세요! {0}에 불이났어요", location);
        }
        static void Escape(string location)
        {
            Console.WriteLine("{0}에서 나갑시다.", location);
        }

호출 함수의 추가는 +=로 이루어지고 제거는 -=로 이루어진다.

ThereIsAFire Fire = new ThereIsAFire(Call119);
Fire += new ThereIsAFire(ShotOut);
Fire += new ThereIsAFire(Escape);

System.Delegate 형식의 정적 멤버함수 Combine을 이용하여 한번에 대리자 체인을 만들 수 있다.

ThereIsAFire Fire2 = (ThereIsAFire)Delegate.Combine(new ThereIsAFire(Call119),
                                                    new ThereIsAFire(ShotOut),
                                                    new ThereIsAFire(Escape));

 

이름없는 메서드를 통한 대리자 등록

람다 함수와 비슷하게 대리자 인스턴스에 인라인된 무명 메서드 선언과 동시에 델리게이트로 선언하여 콜백을 등록할 수 있다.

MyDelegate Func;
Func = delegate (int a, int b) { return a + b; };

 

이벤트 event

클래스 내 멤버로 대리자 인스턴스를 두고 사용하고자 할 때 이벤트 event 키워드를 사용한다. 이벤트라는 다른 타입의 인스턴스가 존재하는 것이 아니라 해당 대리자는 클래스 내에서 사용할 것이라는 캡슐화의 의미를 지닌다. 이벤트로 선언된 대리자 인스턴스는 클래스 외부에서 호출될 수 없다.

    delegate void EventHandler(string message);

    class MyNotifier
    {
        public event EventHandler SomethingHappened;
        public void DoSomething(int number)
        {
            int temp = number % 10;
            if(temp != 0 && temp%3 ==0 )
            {
                // 클래스 내부에서 호출 : 이벤트의 발생
                SomethingHappened(String.Format("{0} : 짝", number));
            }
        }
    }

해당 대리자의 호출은 반드시 DoSomething을 통해서 이루어지며 다음과 같이 클래스 외부에서 호출되는 것을 금지한다.

MyNotifier notifier3 = new MyNotifier();
notifier3.SomethingHappened += MyHandler;

for(int i = 1; i < 30; ++i)
{
	// 이벤트 발생
	notifier3.DoSomething(i);
}

// 에러
notifier3.SomethingHappened("Happened");

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

[C#] LINQ  (0) 2021.10.18
[C#] 람다식  (0) 2021.10.15
[C#] Generic 프로그래밍 / 예외 처리  (0) 2021.10.12
[C#] 배열 / 컬렉션 / 인덱서  (0) 2021.10.11
[C#] 프로퍼티  (0) 2021.10.08