이벤트와 델리게이트 및 해당 애플리케이션의 차이점 [닫힘]


107

구문상의 설탕이되는 것 외에 델리게이트보다 이벤트를 사용하는 것의 이점을 보지 못했습니다. 아마도 오해하고 있을지 모르지만 이벤트는 델리게이트의 자리 표시자인 것 같습니다.

차이점과 사용시기를 설명해 주시겠습니까? 장점과 단점은 무엇입니까? 우리의 코드는 이벤트에 뿌리를두고 있으며, 그 밑바닥에 도달하고 싶습니다.

언제 이벤트에 대리자를 사용하고 그 반대의 경우도 가능합니까? 프로덕션 코드에서 두 가지 모두에 대한 실제 경험을 설명하십시오.


차이가 정말 힘들었다 주위에 네 내 머리를 포장, 그들은 같은 모양과 첫번째보기에서 동일한 작업을 수행하는 것
로버트 굴드

1
이 질문을 참조하십시오 .
Dimitri C.

1
두 이벤트와 대의원의 차이점은 의견이 아니라 사실입니다. 이 질문은 기술이 해결하는 문제의 차이를 보여주기 때문에 각 응용 프로그램에 대해 묻습니다. 아무도 어떤 것이 가장 좋은지 묻지 않았기 때문에 이것은 또한 의견의 문제가 아닙니다. 이 질문의 어떤 부분도 의견의 문제가 아니며이 진술도 의견이 아닙니다. 제 생각에는. 배지를 받았습니까?
Peter Wone

답변:


49

기술적 관점에서 다른 답변은 차이점을 해결했습니다.

의미 론적 관점에서 이벤트는 특정 조건이 충족 될 때 개체에 의해 발생하는 작업입니다. 예를 들어, Stock 클래스에는 Limit라는 속성이 있으며 주가가 Limit에 도달하면 이벤트를 발생시킵니다. 이 알림은 이벤트를 통해 수행됩니다. 누군가가 실제로이 이벤트에 관심을 갖고 구독하는지 여부는 소유자 클래스의 관심을 벗어납니다.

델리게이트는 C / C ++ 용어의 포인터와 유사한 구조를 설명하는보다 일반적인 용어입니다. .Net의 모든 델리게이트는 멀티 캐스트 델리게이트입니다. 의미 론적 관점에서 보면 일반적으로 일종의 입력으로 사용됩니다. 특히 전략 패턴 을 구현하는 완벽한 방법입니다. 입니다. 예를 들어 객체 목록을 정렬하려는 경우 메서드에 Comparator 전략을 제공하여 두 객체를 비교하는 방법을 구현에 알릴 수 있습니다.

프로덕션 코드에서 두 가지 방법을 사용했습니다. 수많은 데이터 개체가 특정 속성이 충족되면이를 알립니다. 가장 기본적인 예는 속성이 변경 될 때마다 PropertyChanged 이벤트가 발생하는 것입니다 (INotifyPropertyChanged 인터페이스 참조). 특정 개체를 문자열로 바꾸는 다양한 전략을 제공하기 위해 코드에서 대리자를 사용했습니다. 이 특정 예제는 사용자에게 표시 할 특정 객체 유형에 대한 구현의 영광스러운 ToString () 목록이었습니다.


4
어쩌면 내가 뭔가를 놓치고 있지만 이벤트 핸들러가 델리게이트 유형이 아닙니까?
Powerlord

1
내 대답은 질문 편집 # 1 및 # 2에 대한 것입니다. 사용 관점에서 차이. 이 논의의 목적 상 기술적 인 관점에서 볼 때 귀하가 맞더라도 서로 다릅니다. 기술적 차이점에 대한 다른 답변을 살펴보십시오.
Szymon Rozga

3
".Net의 모든 델리게이트는 멀티 캐스트 델리게이트"입니까? 값을 반환하는 대리자도?
Qwertie

5
예. 역사는 msdn.microsoft.com/en-us/magazine/cc301816.aspx 를 참조 하십시오 . 확인 : msdn.microsoft.com/en-us/library/system.delegate.aspx . 값을 반환하는 경우 반환되는 값은 체인의 마지막 대리자의 평가입니다.
Szymon Rozga

대리자는 구독자 클래스에 정의 된 이벤트 처리기를 가리키는 참조 형식입니다. 즉, 대리자는 이벤트 (게시자)와 구독자에 정의 된 이벤트 처리기 간의 링크로 사용됩니다. 응용 프로그램에는 여러 구독자가 이벤트를 수신해야하며 이러한 시나리오에서 대리인은 게시자와 구독자를 연결하는 효율적인 방법을 제공합니다.
josepainumkal

55

이 키워드 event는 멀티 캐스트 대리자의 범위 수정 자입니다. 이것과 멀티 캐스트 대리자를 선언하는 것의 실제적인 차이점은 다음과 같습니다.

  • event인터페이스에서 사용할 수 있습니다 .
  • 멀티 캐스트 델리게이트에 대한 호출 액세스는 선언 클래스로 제한됩니다. 동작은 대리자가 호출을 위해 비공개 인 것처럼 보입니다. 할당을 위해 액세스는 명시 적 액세스 수정 자 (예 :)에 의해 지정된 것과 같습니다 public event.

관심의 문제로서, 당신은 적용 할 수 +-대의원을 멀티 캐스트, 이것은의 기초 +=-=이벤트 대표단의 조합 할당을위한 구문. 다음 세 스 니펫은 동일합니다.

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = B + C;

샘플 2는 직접 할당과 조합 할당을 보여줍니다.

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = B;
A += C;

샘플 3 : 더 익숙한 구문. 모든 핸들러를 제거하기 위해 널을 할당하는 것에 익숙 할 것입니다.

B = new EventHandler(this.MethodB);
C = new EventHandler(this.MethodC);
A = null;
A += B;
A += C;

속성과 마찬가지로 이벤트에는 아무도 사용하지 않는 전체 구문이 있습니다. 이:

class myExample 
{
  internal EventHandler eh;

  public event EventHandler OnSubmit 
  { 
    add 
    {
      eh = Delegate.Combine(eh, value) as EventHandler;
    }
    remove
    {
      eh = Delegate.Remove(eh, value) as EventHandler;
    }
  }

  ...
}

... 다음과 정확히 동일합니다.

class myExample 
{
  public event EventHandler OnSubmit;
}

add 및 remove 메서드는 VB.NET이 사용하는 다소 비정상적인 구문에서 더 두드러집니다 (연산자 오버로드 없음).


6
+ "멀티 캐스트 델리게이트에 대한 호출 액세스는 선언 클래스로 제한됩니다"-나에게있어 델리게이트와 이벤트 간의 주요 차이점입니다.
RichardOD

2
또 다른 중요한 차이점 (아래 itowlson에 의해 언급 됨)은 이벤트에 할당하여 모든 이벤트 핸들러를 구독 취소 할 수는 없지만 델리게이트로이를 수행 할 수 있다는 것입니다. (그런데, 당신의 대답은이 모든 것 중에서 나에게 가장 유용한 대답이었습니다).
Roman Starkov

4
Google 및 stackoverflow가 편리 할 수있는 것처럼이 모든 기능은 C # 언어 사양에서 마음에 들지 않는 세부 정보로 제공되며 Microsoft에서 무료로 공개적으로 제공됩니다. 나는 그것의 얼굴에, 하나님이 설명서와 존 소총이 그것을 삼킨 만든 알고 있지만, 다른 사본 :가
피터은 wOne

12

이벤트는 구문 상 설탕입니다. 그것들은 맛있다. 이벤트를 보면 무엇을해야하는지 압니다. 대표자를 보면 잘 모르겠습니다.

이벤트와 인터페이스 (더 많은 설탕)를 결합하면 군침이 도는 간식이됩니다. 델리게이트와 순수 가상 추상 클래스는 훨씬 덜 맛있습니다.


나도 그렇게 봅니다. 나는 :) 더 깊고 달콤한 설명을 원하는

13
설탕이 너무 많으면 지방 하나가 생성되지만 ... = P
Erik Forbes

5

이벤트는 메타 데이터에서 이와 같이 표시됩니다. 이를 통해 Windows Forms 또는 ASP.NET 디자이너는 이벤트를 단순한 대리자 유형의 속성과 구별하고 적절한 지원을 제공 할 수 있습니다 (특히 속성 창의 이벤트 탭에 표시).

대리자 유형의 속성과 다른 또 다른 차이점은 사용자가 이벤트 처리기 만 추가 및 제거 할 수있는 반면 대리자 유형의 속성에서는 값을 설정할 수 있다는 것입니다.

someObj.SomeCallback = MyCallback;  // okay, replaces any existing callback
someObj.SomeEvent = MyHandler;  // not okay, must use += instead

이렇게하면 이벤트 구독자를 분리하는 데 도움이됩니다. 이벤트에 처리기를 추가 할 수 있고 동일한 이벤트에 처리기를 추가 할 수 있으며 실수로 처리기를 덮어 쓰지 않습니다.


4

이벤트는 일반적으로 멀티 캐스트 델리게이트로 구현되지만 이러한 방식으로 사용할 필요는 없습니다. 클래스가 이벤트를 노출하는 경우 이는 클래스가 두 가지 메서드를 노출 함을 의미합니다. 그 의미는 본질적으로 다음과 같습니다.

  1. 여기 대표자가 있습니다. 흥미로운 일이 발생하면 호출하십시오.
  2. 여기 대표자가 있습니다. 편리 할 때에 모든 참조를 폐기해야합니다 (더 이상 호출하지 않음).

클래스가 노출하는 이벤트를 처리하는 가장 일반적인 방법은 멀티 캐스트 델리게이트를 정의하고 위의 메서드에 전달되는 델리게이트를 추가 / 제거하는 것입니다.하지만 그렇게 작동 할 필요는 없습니다. 불행히도 이벤트 아키텍처는 대체 접근 방식을 훨씬 더 깔끔하게 만들 수있는 몇 가지 작업을 수행하지 못하므로 (예 : 구독 메서드가 구독자가 보관할 MethodInvoker를 반환하도록하고, 이벤트 구독을 취소하려면 반환 된 메서드를 호출하기 만하면됩니다) 멀티 캐스트 대리자가됩니다. 가장 일반적인 접근 방식입니다.


4

차이점을 이해하기 위해이 두 가지 예를 볼 수 있습니다.

대리자가있는 예 (이 경우 값을 반환하지 않는 대리자 유형의 작업)

public class Animal
{
    public Action Run {get; set;}

    public void RaiseEvent()
    {
        if (Run != null)
        {
            Run();
        }
    }
}

델리게이트를 사용하려면 다음과 같이해야합니다.

Animale animal= new Animal();
animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running") ;
animal.RaiseEvent();

이 코드는 잘 작동하지만 몇 가지 약점이있을 수 있습니다.

예를 들어 내가 이것을 쓰면

animal.Run += () => Console.WriteLine("I'm running");
animal.Run += () => Console.WriteLine("I'm still running");
animal.Run = () => Console.WriteLine("I'm sleeping") ;

마지막 코드 줄로 이전 동작을 하나만 누락하여 재정의했습니다 +( +대신 사용 했습니다 +=)

또 다른 약점은 당신의 Animal클래스 를 사용하는 모든 클래스가 RaiseEvent그것을 호출하는 것만으로도 올릴 수 있다는 것 animal.RaiseEvent()입니다.

이 약점을 피하기 위해 eventsC #에서 사용할 수 있습니다 .

당신의 동물 클래스는 이런 식으로 변경됩니다

public class ArgsSpecial :EventArgs
   {
        public ArgsSpecial (string val)
        {
            Operation=val;
        }

        public string Operation {get; set;}
   } 



 public class Animal
    {
       public event EventHandler<ArgsSpecial> Run = delegate{} //empty delegate. In this way you are sure that value is always != null because no one outside of the class can change it

       public void RaiseEvent()
       {  
          Run(this, new ArgsSpecial("Run faster"));
       }
    }

이벤트 호출

 Animale animal= new Animal();
 animal.Run += (sender, e) => Console.WriteLine("I'm running. My value is {0}", e.Operation);
 animal.RaiseEvent();

차이점 :

  1. 공용 속성이 아니라 공용 필드 (컴파일러가 원하지 않는 액세스로부터 필드를 보호하는 이벤트 포함)를 사용하고 있습니다.
  2. 이벤트는 직접 할당 할 수 없습니다. 이 경우 동작을 재정 의하여 보여준 이전 오류를 수행 할 수 없습니다.
  3. 클래스 외부의 누구도 이벤트를 제기 할 수 없습니다.
  4. 이벤트는 인터페이스 선언에 포함될 수 있지만 필드는

노트

EventHandler는 다음 대리자로 선언됩니다.

public delegate void EventHandler (object sender, EventArgs e)

보낸 사람 (객체 유형) 및 이벤트 인수를받습니다. 보낸 사람이 정적 메서드에서 가져온 경우 null입니다.

EventHAndler대신이 예제를 사용할 수도 있습니다.EventHandler<ArgsSpecial>

EventHandler에 대한 문서는 여기 를 참조 하십시오.


3

편집 # 1 이벤트 대신 대리자를 언제 사용합니까? 프로덕션 코드에서 두 가지 모두에 대한 실제 경험을 설명하십시오.

자체 API를 디자인 할 때 메서드 또는 클래스 생성자에 매개 변수로 전달되는 대리자를 정의합니다.

  • 따라서 메서드는 간단한 '템플릿 메서드'패턴을 구현할 수 있습니다 (예 : PredicateAction대리자가 .Net 일반 컬렉션 클래스에 전달됨).
  • 또는 클래스가 '콜백'(일반적으로이를 생성 한 클래스의 메서드에 대한 콜백)을 수행 할 수 있도록합니다.

이러한 대리자는 일반적으로 런타임에 선택 사항 이 아닙니다 (즉,이면 안 됨 null).

나는 이벤트를 사용하지 않는 경향이 있습니다. 하지만 이벤트를 사용하는 경우 관심 이 있을 수있는 0 개, 1 개 또는 그 이상의 클라이언트에 이벤트를 선택적으로 신호 하기 위해 이벤트를 사용합니다 . 즉, 클래스 (예 : 클래스)가 존재해야하고 클라이언트가 이벤트에 이벤트 핸들러를 추가했습니다 (예 : 폼의 '마우스 다운'이벤트가 있지만 외부 클라이언트가 해당 이벤트에 이벤트 핸들러를 설치하는 데 관심이 있는지 여부 는 선택 사항 입니다).System.Windows.Form


2

기술적 인 이유는 없지만 UI 스타일 코드, 즉 상위 수준의 코드에서 이벤트를 사용하고 코드에 더 깊은 논리에 대리자를 사용합니다. 둘 중 하나를 사용할 수 있다고 말했듯이이 사용 패턴이 논리적으로 건전하다는 것을 알았습니다. 다른 것이 없다면 콜백 유형과 계층 구조도 문서화하는 데 도움이됩니다.


편집 : 사용 패턴의 차이점은 이벤트를 무시하는 것이 완벽하게 허용되는 것으로 생각합니다. 이벤트에 대해 알아야 할 경우에는 듣고, 신경 쓰지 않으면 후크 / 스텁입니다. 이벤트는 무시합니다. 그래서 자바 스크립트 / 브라우저 이벤트 스타일과 같은 UI에 사용합니다. 그러나 델리게이트가있을 때 누군가가 델리게이트의 작업을 처리하기를 기대하고 처리되지 않으면 예외를 throw합니다.


UI에서도 짝수를 사용하므로 자세히 설명해 주시겠습니까? 좋은 예는 충분할 것입니다 .... 감사합니다

1

이벤트와 델리게이트의 차이는 제가 생각했던 것보다 훨씬 작습니다. 방금 주제에 대한 매우 짧은 YouTube 동영상을 게시했습니다. https://www.youtube.com/watch?v=el-kKK-7SBU

도움이 되었기를 바랍니다!


2
Stack Overflow에 오신 것을 환영합니다! 이 이론적으로 질문에 대답 할 수 있습니다 동안, 바람직 할 것이다 여기에 대한 대답의 본질적인 부분을 포함하고 참조 할 수 있도록 링크를 제공합니다.
GhostCat

1

Event 대신 델리게이트 만 사용하면 구독자는 아래 이미지와 같이 델리게이트 자체를 clone (), invoke () 할 수 있습니다. 옳지 않습니다.

여기에 이미지 설명 입력

그것이 b / w 이벤트와 델리게이트의 주요 차이점입니다. 구독자는 하나의 권한 만 가지고 있습니다. 즉, 이벤트를 듣습니다.

ConsoleLog 클래스는 EventLogHandler를 통해 로그 이벤트를 구독합니다.

public class ConsoleLog
{
    public ConsoleLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write on console : " + str);
    }
}

FileLog 클래스는 EventLogHandler를 통해 로그 이벤트를 구독합니다.

public class FileLog
{
    public FileLog(Operation operation)
    {
        operation.EventLogHandler += print;
    }

    public void print(string str)
    {
        Console.WriteLine("write in File : " + str);
    }
}

작업 클래스는 로그 이벤트를 게시합니다.

public delegate void logDelegate(string str);
public class Operation
{
    public event logDelegate EventLogHandler;
    public Operation()
    {
        new FileLog(this);
        new ConsoleLog(this);
    }

    public void DoWork()
    {
        EventLogHandler.Invoke("somthing is working");
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.