2009-07-13 2 views
20

.NET에서 이벤트에 대한 표준 패턴이 있습니다. 그들은 EventArgs에서 파생되어야하는 두 번째 매개 변수에서 sender라는 일반 객체와 실제 "페이로드"를 취하는 delegate 유형을 사용합니다..NET에서 표준 EventHandler 패턴을 버리면 무엇이 손실됩니까?

EventArgs에서 파생 된 두 번째 매개 변수에 대한 이론적 근거는 매우 명확합니다 (.NET Framework Standard Library Annotated Reference 참조). 소프트웨어가 발전함에 따라 이벤트 싱크와 소스 간의 바이너리 호환성을 보장하기위한 것입니다. 모든 이벤트에 대해 하나의 인수 만 있더라도 해당 인수가 포함 된 단일 속성을 갖는 사용자 정의 이벤트 인수 클래스를 파생하므로 기존 클라이언트 코드를 손상시키지 않고 향후 버전의 페이로드에 더 많은 속성을 추가 할 수 있습니다 . 독립적으로 개발 된 구성 요소의 생태계에서 매우 중요합니다.

하지만 제로 인수에 대해서도 마찬가지입니다. 즉, 첫 번째 버전에서 인수가없는 이벤트가 있고 다음과 같이 쓰는 경우를 의미합니다.

public event EventHandler Click; 

... 그러면 잘못하고 있습니다. 나는 그것의 페이로드로 새로운 클래스에 미래의 대리자 형식을 변경하는 경우 :

public class ClickEventArgs : EventArgs { ... 

을 ... 나는 나의 고객과의 바이너리 호환성을 깰 것입니다. 클라이언트는 EventHandler 걸리는 내부 메서드 add_Click의 특정 오버로드에 바인딩되고, 위임 유형을 변경하면 해당 오버로드를 찾을 수 없으므로 MissingMethodException이 있습니다.

좋아요, 그렇다면 편리한 일반 버전을 사용하면 어떨까요?

public EventHandler<EventArgs> Click; 

없음

, 여전히 잘못 EventHandler<ClickEventArgs>EventHandler<EventArgs>하지 않기 때문에.

EventArgs의 혜택을 얻으려면 이 그대로있는 그대로 사용하는 것이 좋습니다. 당신이하지 않으면, 당신도 그것을 사용하지 않을 수 있습니다 (그것은 나에게 보인다).

그러면 첫 번째 인수 인 sender이 있습니다. 나는 신성하지 않은 결합을위한 조리법을 좋아하는 것 같습니다. 이벤트 발생은 기본적으로 함수 호출입니다. 함수는 일반적으로 말하자면 스택을 파헤쳐 호출자가 누구인지 알아 내고 그에 따라 행동을 조정할 수있는 능력이 있어야합니까? 우리는 인터페이스가 이렇게 보이도록 명령해야합니까?

결국
public interface IFoo 
{ 
    void Bar(object caller, int actualArg1, ...); 
} 

Bar의 구현은 caller이 누구인지 알고 할 수 있습니다, 그래서 그들은 추가 정보를 조회 할 수 있습니다! 네가 지금 엿먹 으리라 믿어. 왜 이벤트를 위해 다른해야합니까?

그럼에도 불구하고 내가 선언 한 모든 이벤트에 대해 독립형 애플리케이션을 만드는 데 어려움을 겪을 준비가 되었더라도 EventArgs을 사용하는 동안 내 가치가있게 만들었습니다. 확실히 개체 발신자 인수를 삭제하는 것이 좋습니다. .

Studio의 자동 완성 기능을 사용하면 이벤트에 사용하는 것을 위임 상관하지 않는 것 같습니다 시각 - 당신은 입력 할 수 있습니다 += [공간, 돌아 히트]를 선택하고 당신을위한 핸들러 방법을 씁니다이 될 일이 무엇이든 위임 경기 .

그렇다면 표준 패턴에서 벗어나서 어떤 가치를 잃을까요?

보너스 질문으로 C#/CLR 4.0은 대리자의 반공 변을 통해이 변경 작업을 수행합니까? 나는 이것을 조사하려고 시도했지만 another problem을 누르십시오. 나는 원래 그 질문의이 부분을 다른 질문에 포함 시켰지만 거기에서 혼란을 야기했다. 그리고 ... 세 가지 질문의 총에이를 분할하는 것이 조금 많은 것 같다

업데이트 :

내가이 모든 문제에 대한 contravariance의 효과에 대해 궁금해하는 것이 옳다고 밝혀!

noted elsewhere과 같이 새로운 컴파일러 규칙은 런타임에 불어 나오는 유형 시스템에 구멍을 남깁니다. EventHandler<T>Action<T>과 다르게 정의하면 구멍이 효과적으로 막혔습니다.

이벤트의 경우 해당 유형 구멍을 피하려면 Action<T>을 사용하지 않아야합니다. 그렇다고해서 EventHandler<TEventArgs>을 사용해야한다는 의미는 아닙니다. 일반 대리자 형식을 사용하는 경우 반 경쟁이 활성화 된 형식을 선택하지 말아야합니다.

+0

downvoter에서 이유를 듣고 싶습니다. –

+0

나는 어떤 방법으로도 투표하지는 않았지만, 여기에 하나 이상의 질문이 적어도 있기 때문에 downvote가 의심된다. 나는 정말로 모든 것을 나 자신으로 다루려고하고 싶지는 않지만, (지금 당장 그것에 대해 쓰고있는 것처럼) 분산 측면을 보아서 기쁘다. 당신이 밖으로을 재현 할 수있는 짧지 만 완전한 케이스 별도의 문제로 실행시 실패에 이르게 비트를 분리 할 수 ​​있다면, 나는 –

+0

가 –

답변

7

아무 것도 잃지 않습니다. 나는 .NET 3.5이 출시 된 이래로 Action<>을 사용 해왔고 훨씬 더 자연스럽고 프로그램하기가 쉽습니다.

I하지 심지어 더 이상 생성 된 이벤트 핸들러에 대한 EventHandler 유형을 다루는, 단순히 람다와 방법 당신이 원하는 서명 및 와이어 그것을 쓰기 :

btnCompleteOrder.OnClick += (o,e) => _presenter.CompleteOrder(); 
+2

익명으로 이벤트를 구독 취소하는 데 문제가 있습니까? – Matt

+2

당신이 원 하겠지만, OnClick 이벤트는 단순히 발표자에게 무슨 일이 있었는지 알리기위한 것이기 때문에 실제로 구독을 취소해서는 안됩니다.가입/탈퇴 중일 경우 비 -ui 클래스에서 발생한 이벤트에 더 관심을 가질 수 있습니다.이 경우 이벤트 유형을 완벽하게 제어 할 수 있으므로 액션 <> 어쨌든 익명의 함수가 필요하지 않습니다. –

+0

@Matt - 나는 그런 식으로 입대하는 나의 이벤트의 대부분을 쓴다. 명시 적으로 구독을 취소 할 필요성은 매우 드뭅니다. 기본적으로 이벤트 소스가 수명이 긴 객체 (예 : 정적 또는 애플리케이션의 기본 창에 저장 됨)이지만 소스와 싱크가 동일한 수명을 갖는 경우, 왜 귀찮은가요? 이것은 GC가 필요한 것입니다. –

1

나는 이벤트 - 마음에 들지을 핸들러 패턴. 내 생각에 Sender 객체는 그다지 도움이되지 않습니다. 이벤트가 어떤 개체 (예 : 변경 알림)에 무슨 일이 일어 났음을 알리는 경우 EventArg에 정보를 보유하는 것이 더 도움이됩니다. Sender가 볼 수있는 유일한 용도는 이벤트 구독을 취소하는 것이지만, 어떤 이벤트를 구독 취소해야하는지 항상 명확하지는 않습니다.

덧붙여 말하면, 내 druthers가 있으면 Event는 AddHandler 메서드 및 RemoveHandler 메서드가되지 않습니다. 등록 취소에 사용할 수있는 MethodInvoker를 반환하는 AddHandler 메소드 일뿐입니다. 보낸 사람 인수 대신 첫 번째 인수는 구독 취소에 필요한 MethodInvoker 사본입니다 (개체가 수신 거부 호출자가 손실 된 이벤트를 수신하는 경우). 표준 MulticastDelegate는 각 구독자가 다른 구독 취소 대리자를 받아야하므로 이벤트를 전달하는 데 적합하지 않지만 구독 취소 이벤트는 호출 목록을 통해 선형 검색을 요구하지 않습니다.