[C#] C #의 이벤트 및 이벤트 핸들러 이해

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

public void EventName(object sender, EventArgs e);

이벤트 핸들러는 무엇을하고 왜 필요하며 어떻게 만들 수 있습니까?



답변

이벤트 핸들러를 이해하려면 델리게이트 를 이해해야 합니다. C # 에서는 대리자를 메서드에 대한 포인터 (또는 참조)로 생각할 수 있습니다. 포인터를 값으로 전달할 수 있기 때문에 유용합니다.

대리인의 중심 개념은 서명 또는 형태입니다. 즉 (1) 반환 유형과 (2) 입력 인수입니다. 우리가 대리자를 만드는 경우 예를 들어 void MyDelegate(object sender, EventArgs e), 그것은 단지 어느 반환 방법을 가리킬 수 있습니다 void및을 object하고 EventArgs. 사각형 구멍과 사각형 못과 같은 종류. 그래서 우리는이 메소드들이 델리게이트와 동일한 서명 또는 형태를 가지고 있다고 말합니다.

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

따라서 기본값 EventHandler(및 그와 비슷한 유형)은 특정 형태의 메소드를 나타냅니다 (다시 말하면 void / object-EventArgs). 이벤트를 선언하면 델리게이트를 지정하여 해당 이벤트를 호출 할 메소드 (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에서 이벤트의 핵심이며 “마법”을 제거합니다. 이벤트는 커버 아래에 같은 “모양”의 메소드 목록 일뿐입니다.이 목록은 이벤트가있는 곳에 저장됩니다. 이벤트가 발생하면 실제로는 “이 메소드 목록을 살펴보고이 값을 매개 변수로 사용하여 각 메소드를 호출합니다.”이벤트 핸들러를 지정하는 것은이 메소드 목록에 메소드를 추가하는 더 예쁘고 쉬운 방법입니다. 호출).


답변

C #은 두 용어를 알고, delegate하고 event. 첫 번째부터 시작하겠습니다.

대리자

A 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
   }

}

따라서 유형을 노출시키지 않고 다른 클래스에 대리자를 사용할 수 있습니다. 노출하는 유일한 방법은 메소드의 서명입니다 (당신이 가진 매개 변수 수 등).

“나의 방법의 서명”, 전에는 어디서 들었습니까? 예, 인터페이스 !!! 인터페이스는 전체 클래스의 서명을 설명합니다. 델리게이트는 한 가지 방법의 서명 만 설명하는 것으로 생각하십시오!

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

또한 대리자 참조는 (일부 제한 사항이 있지만 아래 참조) 여러 메서드 ()를 참조 할 수 있습니다 MulticastDelegate. 이것은 델리게이트를 호출 할 때 명시 적으로 연결된 여러 메소드가 실행됨을 의미합니다. 객체 참조는 항상 하나의 객체 만 참조 할 수 있습니다.

A에 대한 제한은 MulticastDelegate제 (방법 / 대리인) 서명이있는 반환 값 (이없는 것이 있습니다 void) 및 키워드 outref서명에 사용되지 않습니다. 분명히 숫자를 반환하고 같은 숫자를 반환 할 것으로 예상되는 두 가지 메서드를 호출 할 수 없습니다. 서명이 준수되면 대리인은 자동으로 MulticastDelegate입니다.

행사

이벤트는 다른 객체에서 델리게이트에 대한 구독을 표시하는 속성 (예 : get; set; 속성을 인스턴스 필드로)과 같습니다. 그러나 이러한 속성은 get; set;을 지원하지 않습니다. 대신에 그들은 add를 지원합니다; 없애다;

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

    Action myField;

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

UI에서의 사용법 (WinForms, WPF, UWP 등)

이제 델리게이트는 메소드에 대한 참조이며, 델리게이트에서 참조 할 메소드를 제공 할 수 있고 UI 버튼이라는 것을 전세계에 알리는 이벤트를 가질 수 있습니다. 클릭 여부에 관심이있는 모든 사람에게 (우리가 노출 된 이벤트를 통해) 당사에 방법을 등록하도록 요청할 수 있습니다. 우리는 우리에게 주어진 모든 방법을 사용하고 대리인이 참조 할 수 있습니다. 그리고 기다렸다가 기다립니다 …. 사용자가 와서 해당 버튼을 클릭 할 때까지 델리게이트를 호출 할 충분한 이유가 있습니다. 그리고 대리인은 우리에게 주어진 모든 방법을 참조하기 때문에 모든 방법이 호출됩니다. 우리는 그 메소드가 무엇을하는지, 어떤 클래스가 그 메소드를 구현하는지 모릅니다. 우리가 관심을 갖는 것은 누군가 클릭에 관심이 있다는 것입니다.

자바

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


답변

그것은 실제로 이벤트 핸들러에 대한 선언입니다. 이벤트가 시작될 때 호출되는 메소드입니다. 이벤트를 만들려면 다음과 같이 작성하십시오.

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 에 정의 된 일부 이벤트를 보면 추가 정보를 이벤트 핸들러에 전달하는 이벤트의 예를 볼 수 있습니다.


답변

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

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 void MyEventHandler(string foo).

컴파일러는 SomethingHappened 이벤트 의 대리자 유형을 알고 있으므로 다음과 같습니다.

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!");
}

핸들러의 스레드 로컬 복사본이 있는지 확인 호출 스레드 안전이되어 있는지 확인하기 위해 필요 – 그렇지 않으면 스레드가 갈 수있는 것이 있다면 우리가 확인 후 즉시 이벤트의 마지막 핸들러의 등록을 취소 null하고, 우리가 “재미”것 NullReferenceException이 .


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

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


답변

사건에 대한 나의 이해는;

대리자:

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

이벤트 작성 및 호출 단계 :

  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();
            }
        }

    }

}


답변

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

가입자 : 응답이 발생하는 위치. 가입자는 이벤트에 응답 할 방법을 지정해야합니다. 이러한 메소드는 대리자와 동일한 유형의 인수를 사용해야합니다. 그런 다음 구독자는이 방법을 게시자의 대리인에게 추가합니다.

따라서 게시자에서 이벤트가 발생하면 대리인은 일부 이벤트 인수 (데이터 등)를 수신하지만 게시자는 이러한 모든 데이터로 어떤 일이 발생할지 전혀 모릅니다. 가입자는 자신의 클래스에서 메서드를 만들어 게시자 클래스의 이벤트에 응답 할 수 있으므로 가입자는 게시자의 이벤트에 응답 할 수 있습니다.