278

이벤트의 목적, 특히 사용자 인터페이스를 만드는 맥락에서 이해합니다. 나는이 이벤트를 만드는 프로토 타입이라고 생각한다.

public void EventName(object sender, EventArgs e);

이벤트 처리기는 무엇을합니까? 왜 필요합니까? 어떻게해야합니까?


9 답변


566

이벤트 처리기를 이해하려면 이해해야합니다.대의원. 에서기음#대리자를 메서드에 대한 포인터 (또는 참조)로 생각할 수 있습니다. 이것은 포인터가 값으로 전달 될 수 있기 때문에 유용합니다.

델리게이트의 핵심 개념은 서명 또는 모양입니다. 그것은 (1) 반환 유형 및 (2) 입력 인수입니다. 예를 들어 대리인을 만들면void MyDelegate(object sender, EventArgs e), 그것은 리턴하는 메소드만을 가리킬 수있다.void, 그리고objectEventArgs. 일종의 사각 구멍과 사각 나무못 같은 것. 그래서 우리는이 메소드들이 위임자와 동일한 서명이나 모양을 가지고 있다고 말합니다.

따라서 메소드에 대한 참조를 작성하는 방법을 알면 이벤트의 목적을 생각해 봅시다 : 시스템의 다른 곳에서 뭔가가 발생하거나 이벤트를 처리 할 때 일부 코드가 실행되도록하려고합니다. 이를 위해 우리는 실행하고자하는 코드에 대한 특정 메소드를 생성합니다. 이벤트와 실행될 메소드 사이의 연결 고리는 델리게이트입니다. 이벤트는 내부적으로 이벤트가 발생할 때 호출 할 메소드에 대한 포인터의 "목록"을 저장해야합니다. * 물론 메소드를 호출하려면 인수를 전달할 인수를 알아야합니다. 우리는 이벤트와 호출 될 모든 특정 메소드 사이의 "계약"으로서 델리게이트를 사용합니다.

그래서 기본값EventHandler(그리고 많은 것을 좋아한다)는특정 형태의 방법(다시, void / object-EventArgs). 이벤트를 선언하면어떤 형태의 방법대리자를 지정하여 이벤트가 호출 할 EventHandler (EventHandler)

//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyEventHandler(string foo);

//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyEventHandler SomethingHappened;

//Here is some code I want to be executed
//when SomethingHappened fires.
void HandleSomethingHappened(string foo)
{
    //Do some stuff
}

//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

//To raise the event within a method.
SomethingHappened("bar");

(* 이것은 .NET의 이벤트에 대한 열쇠이며 "마법"을 껍질을 벗긴다 - 이벤트는 실제적으로 같은 "모양"의 메소드리스트 일 뿐이며 이벤트가 사는 곳에 저장된다. 이벤트가 "발생"되면 실제로이 메서드 목록을 통해 각 값을 매개 변수로 사용하여 호출합니다. 이벤트 처리기를 할당하는 것은이 메서드 목록에 메서드를 추가하는 것만 큼 쉽고 간단합니다. 호출).


  • 이제 이벤트가 EventHandler라는 이유를 설명 할 수 있습니까? 모든 혼란스러운 명명 규칙 중에서 이것은 최악입니다 ... - Joel in Gö
  • @Joel in 이벤트는 EventHandler라고 불리지 않는다. EventHandler는 이벤트가 그 이벤트와 통신하는 모든 사람에게 있어야하는 계약이다. " 문자열 MyString " - 문자열이 유형을 선언하고 있습니다. 이벤트 MyEventHandler TheEvent는이 이벤트와 상호 작용하는 모든 사용자가 MyEventHandler 계약을 준수해야 함을 선언합니다. 처리기 컨벤션은 주로 계약을 처리하는 방법을 설명하기 때문에 발생합니다. - Rex M
  • 사건은 어떻게 해고됩니까? - alchemical
  • @ 렉스 M : " MyEventHandler "에 대한 첫 번째 일관된 설명에 감사드립니다. 내가 본 것 : - Joel in Gö
  •                         ", 정말 멋지다. - zionpi

92

C #은 두 개의 용어를 알고 있습니다.delegateevent. 첫 번째 작업부터 시작하겠습니다.

대리자

에이delegate메서드에 대한 참조입니다. 인스턴스에 대한 참조를 만들 수있는 것처럼 :

MyClass instance = myFactory.GetInstance();

대리자를 사용하여 메서드에 대한 참조를 만들 수 있습니다.

Action myMethod = myFactory.GetInstance;

이제 메소드에 대한 참조가 있으므로 참조를 통해 메소드를 호출 할 수 있습니다.

MyClass instance = myMethod();

근데 왜 너는? 또한 전화를 걸 수 있습니다.myFactory.GetInstance()직접. 이 경우 당신은 할 수 있습니다. 그러나 응용 프로그램의 나머지 부분에 대한 지식을 원하지 않는 경우를 생각하는 사례가 많이 있습니다.myFactory또는 전화하기myFactory.GetInstance()직접.

분명한 것은 교체 할 수 있는지 여부입니다.myFactory.GetInstance()으로myOfflineFakeFactory.GetInstance()한 곳에서공장 방법 패턴).

공장 방법 패턴

그래서, 만약 당신이TheOtherClass클래스를 사용해야하고myFactory.GetInstance(), 이것은 델리게이트없이 코드가 어떻게 보이게 할 것인가입니다.TheOtherClass너의 타입에 대해 안다.myFactory) :

TheOtherClass toc;
//...
toc.SetFactory(myFactory);


class TheOtherClass
{
   public void SetFactory(MyFactory factory)
   {
      // set here
   }

}

델리게이트를 사용한다면 내 공장 유형을 공개 할 필요가 없습니다.

TheOtherClass toc;
//...
Action factoryMethod = myFactory.GetInstance;
toc.SetFactoryMethod(factoryMethod);


class TheOtherClass
{
   public void SetFactoryMethod(Action factoryMethod)
   {
      // set here
   }

}

따라서, 다른 클래스에 델리게이트를 부여 할 수 있습니다. 노출하는 유일한 방법은 메서드의 서명 (보유한 매개 변수의 개수 등)입니다.

"나의 방법의 서명", 나는 그것을 전에 어디에서 들었습니까? 오 예, 인터페이스 !!! 인터페이스는 전체 클래스의 서명을 기술합니다. 대표자는 단 하나의 방법의 서명을 설명한다고 생각하십시오!

인터페이스와 델리게이트의 또 다른 큰 차이점은 클래스를 작성할 때 C #에 "이 메서드는 해당 델리게이트 유형을 구현합니다"라고 말할 필요가 없다는 것입니다. 인터페이스를 사용하면 "이 클래스는 해당 유형의 인터페이스를 구현합니다"라고 말할 필요가 있습니다.

또한, 델리게이트 참조는 여러 가지 방법을 참조 할 수 있습니다 (일부 제한 사항 있음, 아래 참조).MulticastDelegate). 즉, 델리게이트를 호출하면 명시 적으로 첨부 된 여러 메소드가 실행됩니다. 객체 참조는 항상 하나의 객체만을 참조 할 수 있습니다.

에 대한 제한MulticastDelegate(메서드 / 대리자) 시그니처에는 반환 값이 없어야합니다 (void) 및 키워드outref서명에 사용되지 않습니다. 분명히 숫자를 반환하는 두 개의 메소드를 호출 할 수 없으며 동일한 숫자를 반환 할 것으로 기대합니다. 서명이 준수되면 대리인은 자동으로MulticastDelegate.

행사

이벤트는 다른 객체의 대리자 구독을 노출하는 속성 (예 : get, set, properties 등)입니다. 그러나 이러한 속성은 get; set;을 지원하지 않습니다. 대신 그들은 add; remove;

그래서 당신은 가질 수 있습니다 :

    Action myField;

    public event Action MyProperty
    {
        add { myField += value; }
        remove { myField -= value; }
    }

UI (WinForms)에서의 사용법

이제 대리자는 메서드에 대한 참조이며 대리인에게 참조 할 메서드를 제공 할 수 있다는 사실을 세상에 알릴 수있는 이벤트를 가질 수 있으며 UI 단추입니다. 내가 클릭했는지 여부에 관심이있는 사람에게 우리가 노출 한 이벤트를 통해 자신의 방법을 등록 할 수있는 사람에게 질문 할 수 있습니다. 우리는 우리에게 주어진 모든 방법을 사용할 수 있으며, 우리 대표단을 참조 할 수 있습니다. 그런 다음 사용자가 와서 해당 버튼을 클릭 할 때까지 기다렸다가 기다려야합니다. 그러면 대리인을 호출 할 충분한 이유가 생깁니다. 그리고 델리게이트가 우리에게 주어진 모든 메서드를 참조하기 때문에 모든 메서드가 호출됩니다. 우리는 그 메소드가 무엇을하는지 알지 못하며 어떤 클래스가 그 메소드를 구현하는지도 알지 못한다. 우리가 신경 쓰는 것은 누군가가 우리를 클릭하는 것에 관심을 갖고 우리가 원하는 서명을 따르는 방법에 대한 참조를 주었다는 것입니다.

자바

Java와 같은 언어에는 대리인이 없습니다. 대신 인터페이스를 사용합니다. 그들이하는 일은 '클릭 중'에 관심이있는 사람에게 특정 인터페이스 (우리가 호출 할 수있는 특정 메서드로 구현)를 요구 한 다음 인터페이스를 구현하는 전체 인스턴스를 제공하는 것입니다. 이 인터페이스를 구현하는 모든 객체의 목록을 유지하고 클릭 할 때마다 '호출 할 수있는 특정 메소드'를 호출 할 수 있습니다.


  • 건배에 대한 환호하지만 어떻게 가입자가 걸리는 대리인의 인스턴스와 다른 이벤트가 발생합니까? 그들은 둘 다 똑같은 것처럼 보입니까? - BKSpurgeon
  • @BK 스펄전은 그것 때문에아르" 가입자를 유치하는 대표자 " -event문법 설탕 일 뿐이야. - Mathieu Guindon
  • " MulticastDelegate에 대한 제한 사항은 (메소드 / 위임) 시그니처에 반환 값 (void)이 없어야한다는 것입니다. ", 이것이 정확하다고 생각하지 않습니다. 반환 값이 있으면 마지막 값을 반환합니다. - Hozikimaru

38

다음은 도움이 될 수있는 코드 예제입니다.

using System;
using System.Collections.Generic;
using System.Text;

namespace Event_Example
{
  // First we have to define a delegate that acts as a signature for the
  // function that is ultimately called when the event is triggered.
  // You will notice that the second parameter is of MyEventArgs type.
  // This object will contain information about the triggered event.

  public delegate void MyEventHandler(object source, MyEventArgs e);

  // This is a class which describes the event to the class that receives it.
  // An EventArgs class must always derive from System.EventArgs.

  public class MyEventArgs : EventArgs
  {
    private string EventInfo;

    public MyEventArgs(string Text) {
      EventInfo = Text;
    }

    public string GetInfo() {
      return EventInfo;
    }
  }

  // This next class is the one which contains an event and triggers it
  // once an action is performed. For example, lets trigger this event
  // once a variable is incremented over a particular value. Notice the
  // event uses the MyEventHandler delegate to create a signature
  // for the called function.

  public class MyClass
  {
    public event MyEventHandler OnMaximum;

    private int i;
    private int Maximum = 10;

    public int MyValue
    {
      get { return i; }
      set
      {
        if(value <= Maximum) {
          i = value;
        }
        else 
        {
          // To make sure we only trigger the event if a handler is present
          // we check the event to make sure it's not null.
          if(OnMaximum != null) {
            OnMaximum(this, new MyEventArgs("You've entered " +
              value.ToString() +
              ", but the maximum is " +
              Maximum.ToString()));
          }
        }
      }
    }
  }

  class Program
  {
    // This is the actual method that will be assigned to the event handler
    // within the above class. This is where we perform an action once the
    // event has been triggered.

    static void MaximumReached(object source, MyEventArgs e) {
      Console.WriteLine(e.GetInfo());
    }

    static void Main(string[] args) {
      // Now lets test the event contained in the above class.
      MyClass MyObject = new MyClass();
      MyObject.OnMaximum += new MyEventHandler(MaximumReached);
      for(int x = 0; x <= 15; x++) {
        MyObject.MyValue = x;
      }
      Console.ReadLine();
    }
  }
}


  • Delegate 호출은 C #6에서 다음과 같이 단순화 될 수 있습니다.OnMaximum?.Invoke(this,new MyEventArgs("you've entered...")); - Rango

32

실제로는 이벤트 핸들러에 대한 선언입니다. 이벤트는 이벤트가 발생하면 호출됩니다. 이벤트를 만들려면 다음과 같이 작성하십시오.

public class Foo
{
    public event EventHandler MyEvent;
}

그러면 다음과 같은 이벤트에 가입 할 수 있습니다.

Foo foo = new Foo();
foo.MyEvent += new EventHandler(this.OnMyEvent);

OnMyEvent ()는 다음과 같이 정의됩니다.

private void OnMyEvent(object sender, EventArgs e)
{
    MessageBox.Show("MyEvent fired!");
}

할때는 언제나Foo발사하다MyEvent, 그럼 네가OnMyEvent핸들러가 호출됩니다.

항상 인스턴스를 사용할 필요는 없습니다.EventArgs두 번째 매개 변수로 추가 정보를 포함하려면 다음에서 파생 된 클래스를 사용할 수 있습니다.EventArgs(EventArgs관례 상 기본이다). 예를 들어,에 정의 된 이벤트 중 일부를ControlWinForms에서FrameworkElementWPF에서 이벤트 처리기에 추가 정보를 전달하는 이벤트의 예를 볼 수 있습니다.


  • 질문에 답하고 대표자 및 행사에 참석하지 않으셔서 감사드립니다. - divide_byzero
  • 나는 이것을 사용하지 말 것을 권한다.OnXXX이벤트 핸들러에 대한 이름 지정 패턴. (어리석게도, OnXXX는 MFC에서 XXX을 처리하고,   .net에서 XXX을   처리한다는 의미로 받아 들여졌으며, 이제 그 의미는 불분명하고 혼동 스럽습니다.자세한 내용은이 게시물을 참조하십시오.). 선호하는 이름은RaiseXXX사건을 일으키고HandleXXX또는Sender_XXX이벤트 처리기 용. - Jason Williams
  • 간단한 WinForms 응용 프로그램으로 작업 예제를 보여줄 수 있습니까? - MC9000

21

기존의 위대한 답변을 여기에 추가하기 만하면됩니다. 허용 된 코드의 코드를 기반으로delegate void MyEventHandler(string foo)...

컴파일러는뭔가가 있었어.이벤트,이 :

myObj.SomethingHappened += HandleSomethingHappened;

완전히 동등합니다 :

myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

또한 핸들러는미등록의-=이렇게 :

// -= removes the handler from the event's list of "listeners":
myObj.SomethingHappened -= HandleSomethingHappened;

완전성을 위해 이벤트를 소유하는 클래스에서만 다음과 같이 이벤트를 발생시킬 수 있습니다.

//Firing the event is done by simply providing the arguments to the event:
var handler = SomethingHappened; // thread-local copy of the event
if (handler != null) // the event is null if there are no listeners!
{
    handler("Hi there!");
}

호출자가 thread-safe한지 확인하기 위해서는 핸들러의 스레드 로컬 복사본이 필요합니다. 그렇지 않으면 스레드는 해당 이벤트가 있는지 확인한 후 즉시 이벤트의 마지막 핸들러를 등록 취소 할 수 있습니다null, 그리고 우리는 "재미"NullReferenceException그곳에.


C #6은이 패턴에 대해 좋은 짧은 손을 소개했습니다. 널 전파 연산자를 사용합니다.

SomethingHappened?.Invoke("Hi there!");


11

사건에 대한 나의 이해는;

대리자:

실행될 메소드 / 메소드에 대한 참조를 보유 할 변수. 이렇게하면 변수와 같은 메소드를 전달할 수 있습니다.

이벤트 생성 및 호출 단계 :

  1. 이벤트는 델리게이트의 인스턴스입니다.

  2. 이벤트는 델리게이트의 인스턴스이기 때문에 먼저 델리게이트를 정의해야합니다.

  3. 이벤트가 발생했을 때 실행될 메소드 / 메소드 지정대의원에게 전화하기)

  4. 이벤트를 발생시킵니다 (대의원에게 전화)

예:

using System;

namespace test{
    class MyTestApp{
        //The Event Handler declaration
        public delegate void EventHandler();

        //The Event declaration
        public event EventHandler MyHandler;

        //The method to call
        public void Hello(){
            Console.WriteLine("Hello World of events!");
        }

        public static void Main(){
            MyTestApp TestApp = new MyTestApp();

            //Assign the method to be called when the event is fired
            TestApp.MyHandler = new EventHandler(TestApp.Hello);

            //Firing the event
            if (TestApp.MyHandler != null){
                TestApp.MyHandler();
            }
        }

    }   

}


3

게시자 : 이벤트가 발생하는 곳입니다. 게시자는 클래스가 사용하는 대리자를 지정하고 필요한 인수를 생성하고 해당 인수와 자체를 대리자에게 전달해야합니다.

subscriber : 응답이 일어나는 곳. 구독자는 이벤트에 응답하는 메서드를 지정해야합니다. 이러한 메서드는 대리자와 같은 형식의 인수를 사용해야합니다. 구독자는이 메서드를 게시자의 대리자에 추가합니다.

따라서 게시자에서 이벤트가 발생하면 대리인은 일부 이벤트 인수 (데이터 등)를 받게되지만 게시자는 이러한 모든 데이터가 어떻게 될지 전혀 알 수 없습니다. 구독자는 게시자 클래스의 이벤트에 응답하기 위해 자체 클래스에서 메서드를 만들어 구독자가 게시자의 이벤트에 응답 할 수 있습니다.


2

//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyDelegate(string foo);

//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyDelegate MyEvent;

//Here is some code I want to be executed
//when SomethingHappened fires.
void MyEventHandler(string foo)
{
    //Do some stuff
}

//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.MyEvent += new MyDelegate (MyEventHandler);


0

나는 KE50에 동의하는데, '이벤트'키워드는 'ActionCollection'에 대한 별칭으로 볼 수있다. 왜냐하면 이벤트는 수행 될 액션들의 집합 (즉, 델리게이트)을 갖고 있기 때문이다.

using System;

namespace test{

class MyTestApp{
    //The Event Handler declaration
    public delegate void EventAction();

    //The Event Action Collection 
    //Equivalent to 
    //  public List<EventAction> EventActions=new List<EventAction>();
    //        
    public event EventAction EventActions;

    //An Action
    public void Hello(){
        Console.WriteLine("Hello World of events!");
    }
    //Another Action
    public void Goodbye(){
        Console.WriteLine("Goodbye Cruel World of events!");
    }

    public static void Main(){
        MyTestApp TestApp = new MyTestApp();

        //Add actions to the collection
        TestApp.EventActions += TestApp.Hello;
        TestApp.EventActions += TestApp.Goodbye;

        //Invoke all event actions
        if (TestApp.EventActions!= null){
            //this peculiar syntax hides the invoke 
            TestApp.EventActions();
            //using the 'ActionCollection' idea:
            // foreach(EventAction action in TestApp.EventActions)
            //     action.Invoke();
        }
    }

}   

}

연결된 질문


최근 질문