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
) 및 키워드 out
와 ref
서명에 사용되지 않습니다. 분명히 숫자를 반환하고 같은 숫자를 반환 할 것으로 예상되는 두 가지 메서드를 호출 할 수 없습니다. 서명이 준수되면 대리인은 자동으로 MulticastDelegate
입니다.
행사
이벤트는 다른 객체의 델리게이트에 대한 구독을 표시하는 속성 (예 : get; set; 속성을 인스턴스 필드로)입니다. 그러나 이러한 속성은 get; set;을 지원하지 않습니다. 대신에 그들은 add를 지원합니다; 없애다;
그래서 당신은 가질 수 있습니다 :
Action myField;
public event Action MyProperty
{
add { myField += value; }
remove { myField -= value; }
}
UI에서의 사용법 (WinForms, WPF, UWP 등)
이제 델리게이트는 메소드에 대한 참조이며, 델리게이트에서 참조 할 메소드를 우리에게 제공 할 수 있고 UI 버튼이라는 것을 전세계에 알리는 이벤트를 가질 수 있음을 알고 있습니다. 클릭 여부에 관심이있는 모든 사람에게 (우리가 노출 된 이벤트를 통해) 방법을 등록하도록 요청할 수 있습니다. 우리는 우리에게 주어진 모든 방법을 사용하고 대리인이 참조 할 수 있습니다. 그리고 기다렸다가 기다립니다 .... 사용자가 와서 해당 버튼을 클릭 할 때까지 델리게이트를 호출 할 충분한 이유가 있습니다. 그리고 대리인은 우리에게 주어진 모든 방법을 참조하기 때문에 모든 방법이 호출됩니다. 우리는 그 메소드가 무엇을하는지, 어떤 클래스가 그 메소드를 구현하는지 모릅니다. 우리가 관심을 갖는 것은 누군가 클릭에 관심이 있다는 것입니다.
자바
Java와 같은 언어에는 대리자가 없습니다. 대신 인터페이스를 사용합니다. 그들이하는 방법은 '우리의 클릭'에 관심이있는 사람에게 특정 인터페이스 (우리가 호출 할 수있는 특정 방법으로)를 구현하도록 요청한 다음 인터페이스를 구현하는 전체 인스턴스를 제공하는 것입니다. 우리는이 인터페이스를 구현하는 모든 객체의 목록을 유지하며 클릭 할 때마다 '호출 할 수있는 확실한 메소드'를 호출 할 수 있습니다.
다음은 도움이 될 수있는 코드 예제입니다.
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();
}
}
}
그것은 실제로 이벤트 핸들러에 대한 선언입니다. 이벤트가 시작될 때 호출되는 메소드입니다. 이벤트를 만들려면 다음과 같이 작성하십시오.
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
규칙은 기본입니다). 예를 들어, Control
WinForms 또는 FrameworkElement
WPF 에 정의 된 일부 이벤트를 보면 추가 정보를 이벤트 핸들러에 전달하는 이벤트의 예를 볼 수 있습니다.
여기에 기존의 훌륭한 답변을 추가하려면-허용되는 코드를 작성하십시오 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!");
사건에 대한 나의 이해는;
대리자:
실행될 메소드 / 메소드에 대한 참조를 보유하는 변수입니다. 이를 통해 변수와 같은 메소드를 전달할 수 있습니다.
이벤트 작성 및 호출 단계 :
이벤트는 대리인의 인스턴스입니다
이벤트는 델리게이트의 인스턴스이므로 먼저 델리게이트를 정의해야합니다.
이벤트가 발생할 때 실행할 메소드 / 메소드 지정 ( 대리자 호출 )
이벤트 시작 ( 대리인에게 전화 )
예:
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();
}
}
}
}
출판사 : 이벤트가 일어나는 곳. 게시자는 클래스에서 사용중인 대리자를 지정하고 필요한 인수를 생성하고 해당 인수와 자체를 대리자에게 전달해야합니다.
가입자 : 응답이 발생하는 위치. 가입자는 이벤트에 응답 할 방법을 지정해야합니다. 이러한 메소드는 대리자와 동일한 유형의 인수를 사용해야합니다. 그런 다음 구독자는이 방법을 게시자의 대리인에게 추가합니다.
따라서 게시자에서 이벤트가 발생하면 대리인은 일부 이벤트 인수 (데이터 등)를 수신하지만 게시자는 이러한 모든 데이터로 어떤 일이 발생할지 전혀 모릅니다. 가입자는 자신의 클래스에서 메서드를 만들어 게시자 클래스의 이벤트에 응답 할 수 있으므로 가입자는 게시자의 이벤트에 응답 할 수 있습니다.
//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);
이벤트에 수행 할 동작 (예 : 대리자) 모음이 있으므로 'event'키워드를 'ActionCollection'의 별칭으로 간주한다는 점을 제외하고는 KE50에 동의합니다.
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();
}
}
}
}
게시물에 큰 기술 답변! 나는 아무 것도 없다 기술적으로 그에 추가 할 수 있습니다.
새로운 기능이 일반적으로 언어 및 소프트웨어로 나타나는 주된 이유 중 하나는 마케팅 또는 회사 정치입니다! :-) 예상치 못한 수치입니다!
나는 이것이 대리인과 이벤트에도 적용된다고 생각합니다! 나는 그것들이 유용하고 C # 언어에 가치를 더하지만 Java 언어는 사용하지 않기로 결정했습니다! 그들은 당신이 델리게이트로 해결하고있는 어떤 것이라도 언어의 기존 기능, 즉 인터페이스로 이미 해결할 수 있다고 결정했습니다.
2001 년경 Microsoft는 Java에 대한 경쟁 업체 솔루션으로 .NET 프레임 워크와 C # 언어를 출시 했으므로 Java에없는 새로운 기능을 갖추는 것이 좋습니다.
최근에 C #에서 이벤트를 사용하는 방법에 대한 예를 만들어 내 블로그에 게시했습니다. 나는 아주 간단한 예를 들어 가능한 한 명확하게 만들려고 노력했다. 누군가를 도울 수있는 경우 여기에 있습니다 : http://www.konsfik.com/using-events-in-csharp/
여기에는 설명과 소스 코드 (많은 주석 포함)가 포함되며 주로 이벤트 및 이벤트 핸들러의 적절한 (템플릿과 같은) 사용법에 중점을 둡니다.
몇 가지 핵심 사항은 다음과 같습니다.
이벤트는 "하위 유형의 델리게이트"와 유사하며 더 제한적입니다 (좋은 방법으로). 실제로 이벤트 선언에는 항상 델리게이트가 포함됩니다 (EventHandlers는 델리게이트 유형입니다).
이벤트 핸들러는 특정 유형의 델리게이트 (템플릿으로 생각할 수 있음)로, 사용자가 특정 "서명"을 갖는 이벤트를 작성하도록합니다. 서명의 형식은 다음과 같습니다 (개체 전송자, EventArgs eventarguments).
이벤트가 전달하는 데 필요한 모든 유형의 정보를 포함하기 위해 자체 하위 클래스의 EventArgs를 작성할 수 있습니다. 이벤트를 사용할 때 EventHandlers를 사용할 필요는 없습니다. 당신은 그것들을 완전히 건너 뛰고 대신에 당신 자신의 종류의 대리인을 사용할 수 있습니다.
이벤트와 델리게이트 사용의 한 가지 주요 차이점은 이벤트는 공개로 선언 된 경우에도 선언 된 클래스 내에서만 이벤트를 호출 할 수 있다는 것입니다. 이는 이벤트가 외부 메소드에 "연결되어"동시에 "외부 오용"으로부터 보호 될 수 있도록하기 때문에 매우 중요한 차이점입니다.
참고 URL : https://stackoverflow.com/questions/803242/understanding-events-and-event-handlers-in-c-sharp
'Programing' 카테고리의 다른 글
Git을 사용하여 하나의 명령으로 여러 분기를 삭제할 수 있습니까? (0) | 2020.03.13 |
---|---|
Sublime Text 2에서 4 개의 공백을 탭으로 바꾸는 방법은 무엇입니까? (0) | 2020.03.13 |
동적 키로 객체 생성 (0) | 2020.03.13 |
백그라운드에서 Android 애플리케이션이 실행 중인지 확인 (0) | 2020.03.13 |
PHP를 사용하여 HTTP 헤더를 UTF-8로 설정 (0) | 2020.03.13 |