구문상의 설탕이되는 것 외에 델리게이트보다 이벤트를 사용하는 것의 이점을 보지 못했습니다. 아마도 오해하고 있을지 모르지만 이벤트는 델리게이트의 자리 표시자인 것 같습니다.
차이점과 사용시기를 설명해 주시겠습니까? 장점과 단점은 무엇입니까? 우리의 코드는 이벤트에 뿌리를두고 있으며, 그 밑바닥에 도달하고 싶습니다.
언제 이벤트에 대리자를 사용하고 그 반대의 경우도 가능합니까? 프로덕션 코드에서 두 가지 모두에 대한 실제 경험을 설명하십시오.
구문상의 설탕이되는 것 외에 델리게이트보다 이벤트를 사용하는 것의 이점을 보지 못했습니다. 아마도 오해하고 있을지 모르지만 이벤트는 델리게이트의 자리 표시자인 것 같습니다.
차이점과 사용시기를 설명해 주시겠습니까? 장점과 단점은 무엇입니까? 우리의 코드는 이벤트에 뿌리를두고 있으며, 그 밑바닥에 도달하고 싶습니다.
언제 이벤트에 대리자를 사용하고 그 반대의 경우도 가능합니까? 프로덕션 코드에서 두 가지 모두에 대한 실제 경험을 설명하십시오.
답변:
기술적 관점에서 다른 답변은 차이점을 해결했습니다.
의미 론적 관점에서 이벤트는 특정 조건이 충족 될 때 개체에 의해 발생하는 작업입니다. 예를 들어, Stock 클래스에는 Limit라는 속성이 있으며 주가가 Limit에 도달하면 이벤트를 발생시킵니다. 이 알림은 이벤트를 통해 수행됩니다. 누군가가 실제로이 이벤트에 관심을 갖고 구독하는지 여부는 소유자 클래스의 관심을 벗어납니다.
델리게이트는 C / C ++ 용어의 포인터와 유사한 구조를 설명하는보다 일반적인 용어입니다. .Net의 모든 델리게이트는 멀티 캐스트 델리게이트입니다. 의미 론적 관점에서 보면 일반적으로 일종의 입력으로 사용됩니다. 특히 전략 패턴 을 구현하는 완벽한 방법입니다. 입니다. 예를 들어 객체 목록을 정렬하려는 경우 메서드에 Comparator 전략을 제공하여 두 객체를 비교하는 방법을 구현에 알릴 수 있습니다.
프로덕션 코드에서 두 가지 방법을 사용했습니다. 수많은 데이터 개체가 특정 속성이 충족되면이를 알립니다. 가장 기본적인 예는 속성이 변경 될 때마다 PropertyChanged 이벤트가 발생하는 것입니다 (INotifyPropertyChanged 인터페이스 참조). 특정 개체를 문자열로 바꾸는 다양한 전략을 제공하기 위해 코드에서 대리자를 사용했습니다. 이 특정 예제는 사용자에게 표시 할 특정 객체 유형에 대한 구현의 영광스러운 ToString () 목록이었습니다.
이 키워드 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이 사용하는 다소 비정상적인 구문에서 더 두드러집니다 (연산자 오버로드 없음).
이벤트는 구문 상 설탕입니다. 그것들은 맛있다. 이벤트를 보면 무엇을해야하는지 압니다. 대표자를 보면 잘 모르겠습니다.
이벤트와 인터페이스 (더 많은 설탕)를 결합하면 군침이 도는 간식이됩니다. 델리게이트와 순수 가상 추상 클래스는 훨씬 덜 맛있습니다.
이벤트는 메타 데이터에서 이와 같이 표시됩니다. 이를 통해 Windows Forms 또는 ASP.NET 디자이너는 이벤트를 단순한 대리자 유형의 속성과 구별하고 적절한 지원을 제공 할 수 있습니다 (특히 속성 창의 이벤트 탭에 표시).
대리자 유형의 속성과 다른 또 다른 차이점은 사용자가 이벤트 처리기 만 추가 및 제거 할 수있는 반면 대리자 유형의 속성에서는 값을 설정할 수 있다는 것입니다.
someObj.SomeCallback = MyCallback; // okay, replaces any existing callback
someObj.SomeEvent = MyHandler; // not okay, must use += instead
이렇게하면 이벤트 구독자를 분리하는 데 도움이됩니다. 이벤트에 처리기를 추가 할 수 있고 동일한 이벤트에 처리기를 추가 할 수 있으며 실수로 처리기를 덮어 쓰지 않습니다.
이벤트는 일반적으로 멀티 캐스트 델리게이트로 구현되지만 이러한 방식으로 사용할 필요는 없습니다. 클래스가 이벤트를 노출하는 경우 이는 클래스가 두 가지 메서드를 노출 함을 의미합니다. 그 의미는 본질적으로 다음과 같습니다.
클래스가 노출하는 이벤트를 처리하는 가장 일반적인 방법은 멀티 캐스트 델리게이트를 정의하고 위의 메서드에 전달되는 델리게이트를 추가 / 제거하는 것입니다.하지만 그렇게 작동 할 필요는 없습니다. 불행히도 이벤트 아키텍처는 대체 접근 방식을 훨씬 더 깔끔하게 만들 수있는 몇 가지 작업을 수행하지 못하므로 (예 : 구독 메서드가 구독자가 보관할 MethodInvoker를 반환하도록하고, 이벤트 구독을 취소하려면 반환 된 메서드를 호출하기 만하면됩니다) 멀티 캐스트 대리자가됩니다. 가장 일반적인 접근 방식입니다.
차이점을 이해하기 위해이 두 가지 예를 볼 수 있습니다.
대리자가있는 예 (이 경우 값을 반환하지 않는 대리자 유형의 작업)
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();
차이점 :
노트
EventHandler는 다음 대리자로 선언됩니다.
public delegate void EventHandler (object sender, EventArgs e)
보낸 사람 (객체 유형) 및 이벤트 인수를받습니다. 보낸 사람이 정적 메서드에서 가져온 경우 null입니다.
EventHAndler대신이 예제를 사용할 수도 있습니다.EventHandler<ArgsSpecial>
편집 # 1 이벤트 대신 대리자를 언제 사용합니까? 프로덕션 코드에서 두 가지 모두에 대한 실제 경험을 설명하십시오.
자체 API를 디자인 할 때 메서드 또는 클래스 생성자에 매개 변수로 전달되는 대리자를 정의합니다.
Predicate및 Action대리자가 .Net 일반 컬렉션 클래스에 전달됨).이러한 대리자는 일반적으로 런타임에 선택 사항 이 아닙니다 (즉,이면 안 됨 null).
나는 이벤트를 사용하지 않는 경향이 있습니다. 하지만 이벤트를 사용하는 경우 관심 이 있을 수있는 0 개, 1 개 또는 그 이상의 클라이언트에 이벤트를 선택적으로 신호 하기 위해 이벤트를 사용합니다 . 즉, 클래스 (예 : 클래스)가 존재해야하고 클라이언트가 이벤트에 이벤트 핸들러를 추가했습니다 (예 : 폼의 '마우스 다운'이벤트가 있지만 외부 클라이언트가 해당 이벤트에 이벤트 핸들러를 설치하는 데 관심이 있는지 여부 는 선택 사항 입니다).System.Windows.Form
기술적 인 이유는 없지만 UI 스타일 코드, 즉 상위 수준의 코드에서 이벤트를 사용하고 코드에 더 깊은 논리에 대리자를 사용합니다. 둘 중 하나를 사용할 수 있다고 말했듯이이 사용 패턴이 논리적으로 건전하다는 것을 알았습니다. 다른 것이 없다면 콜백 유형과 계층 구조도 문서화하는 데 도움이됩니다.
편집 : 사용 패턴의 차이점은 이벤트를 무시하는 것이 완벽하게 허용되는 것으로 생각합니다. 이벤트에 대해 알아야 할 경우에는 듣고, 신경 쓰지 않으면 후크 / 스텁입니다. 이벤트는 무시합니다. 그래서 자바 스크립트 / 브라우저 이벤트 스타일과 같은 UI에 사용합니다. 그러나 델리게이트가있을 때 누군가가 델리게이트의 작업을 처리하기를 기대하고 처리되지 않으면 예외를 throw합니다.
이벤트와 델리게이트의 차이는 제가 생각했던 것보다 훨씬 작습니다. 방금 주제에 대한 매우 짧은 YouTube 동영상을 게시했습니다. https://www.youtube.com/watch?v=el-kKK-7SBU
도움이 되었기를 바랍니다!
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");
}
}