프로그래밍/C#

[C#] event

갓똥 2020. 4. 8. 16:51
728x90
반응형

1. 개요

using System;

delegate void HANDLER();

class Button {
    public HANDLER handler = null;
    
    public void press() {
        handler();
    }
}

class Program {
    static void Main() {
        Button btn = new Button();
        btn.press();
    }
    public static void F1() { Console.WriteLine("F1"); }
    public static void F2() { Console.WriteLine("F2"); }
}

 

위와 같은 코드가 있다고 생각을 해보자.
Button클래스는 실제 GUI에서 사용할 버튼이라고 생각하고
Main에서 Button객체를 만들고 press를 호출하는데
실제 사용할 때는 사용자가 눌러서 실행된다고 생각을 해보자.
그럼 Main에서 btn.handler에 원하는 메소드를 넣어 실행시킬 수 있다.
Delegate Combine으로 여러개의 메소드를 부를 수도 있다.
using System;

delegate void HANDLER();

class Button {
    public HANDLER handler = null;
    
    public void press() {
        handler();
    }
}

class Program {
    static void Main() {
        Button btn = new Button();
        btn.handler = F1;
        btn.handler += F2;
        btn.press();
    }
    public static void F1() { Console.WriteLine("F1"); }
    public static void F2() { Console.WriteLine("F2"); }
}

 

그런데 문제가 하나 있다.
handler는 null로 초기화 되어있는데 Main에서 아무런 메소드도 넣지 않는다면
null을 호출해 에러가 날 것이다.
null인지를 판단해야하는데 앞서 작성한 포스팅에 elvis operator가 있다. (https://dhshin94.tistory.com/83)
하지만 handler?(); 로 사용해보면 에러가 난다. 
왜냐하면 elvis operator는 ?. 또는 ?[ 의 형태에서만 가능하기 때문이다.
이땐 표현을 좀 바꾸어 handler?.Invoke(); 로 해결 가능하다.
using System;

delegate void HANDLER();

class Button {
    public HANDLER handler = null;
    
    public void press() {
        // ?.  ?[
        // handler?();  - error
        handler?.Invoke();
    }
}

class Program {
    static void Main() {
        Button btn = new Button();
        btn.handler = F1;
        btn.handler += F2;
        btn.press();
    }
    public static void F1() { Console.WriteLine("F1"); }
    public static void F2() { Console.WriteLine("F2"); }
}

 

이제 코드의 문제는 사라졌는데 사용자 입장에서 문제가 있다.
해당 버튼에 여러 기능을 구현하려 여러사람이 메소드를 추가하는데
19번째 라인의 btn.handler += F2; 에서 += 을 쓴다는게 =만 썼다고 해보자.
그럼 전에 작성한 F1은 날라가고 F2만 남게된다.
F2를 작성한 사람과 F1을 작성한 사람의 독립성이 없이 연결되어 있어 생기는 문제이다.
이 때 사용하면 되는 것이 event이다.
event를 사용하게 되면 더 이상 =은 사용할 수 없고 오직 +=만 사용할 수 있다.
=를 사용할경우 컴파일러 에러가 난다.
using System;

delegate void HANDLER();

class Button {
    public event HANDLER handler = null;
    
    public void press() {
        // ?.  ?[
        // handler?();  - error
        handler?.Invoke();
    }
}

class Program {
    static void Main() {
        Button btn = new Button();
        btn.handler = F1;  // error
        btn.handler += F1; // ok
        btn.handler += F2; // ok
        btn.handler = F2;  // error
        btn.press();
    }
    public static void F1() { Console.WriteLine("F1"); }
    public static void F2() { Console.WriteLine("F2"); }
}

 ① event

    => 구독자(Subscriber)의 독립성을 제공하기 위한 도구

    => +=, -= 로만 메소드를 등록할 수 있다.

 


2. 원리

using System;

delegate void HANDLER();

/*
class Button {
    public event HANDLER handler = null;
    
    public void press() {
        handler?.Invoke();
    }
}
*/

class Button {
    private HANDLER handler = null;
    
    public void add_handler(HANDLER f) { handler += f; }
    public void remove_handler(HANDELR f) { handler -= f; }
    
    public void press() {
        handler?.Invoke();
    }
}

class Program {
    static void Main() {
        Button btn = new Button();
        btn.handler = F1;  // error
        btn.handler += F2; // btn.add_handler(F2);
        btn.press();
    }
    public static void F1() { Console.WriteLine("F1"); }
    public static void F2() { Console.WriteLine("F2"); }
}

 

event의 원리를 파악해보도록 하자.
event를 적으면 컴파일러가 어떤식으로 변경시키는지 간단하게 알아보면 위와 같다.
public필드였던 handler는 private필드로 바뀌게 되고
add_handler와 remove_handler가 생기게 된다.
private이기 때문에 직접 접근 시 에러가 나게 된다.
btn.handler += F1; 과 같이 접근하면 컴파일러가 해당 코드는 btn.add_handler(F1);과 같이 변경 시킨다.
ILDASM으로 event가 있을 때와 없을 때를 비교해보면 아래와 같다.

왼쪽 이미지가 event가 없을 때고, 오른쪽 이미지가 event를 사용했을 때이다.
Button클래스를 보면 handler가 각각 public과 private인 것을 확인할 수 있다.

각각 직접 접근하는 것과 add_handler를 통해 접근하는게 확인된다.

 ② event의 원리

    => add_handler() / remove_handler() 메소드가 생성

728x90
반응형