2008-10-20 4 views
5

위의 두 가지 기능의 대리자, 이벤트 및 .NET 구현과 관련하여 스택 오버플로에 대한 몇 가지 좋은 질문을 보았습니다. 특히 한 가지 질문 인 "How do C# Events work behind the scenes?"은 몇 가지 미묘한 점을 잘 설명해주는 훌륭한 답을 제공했습니다. 당신은 필드와 같은 이벤트 이 ... 컴파일러는 동일한 유형 의 방법 및 개인 필드를 (생성 선언 할 때이벤트가 .NET에서 델리게이트로 구현되는 경우 .event IL 섹션의 요점은 무엇입니까?

:

위의 질문에 대한 답변은이 점을 만든다 대리자). 클래스 내에서 ElementAddedEvent를 참조 할 때 필드를 참조하고 있습니다. 클래스 밖에서, 당신은 필드를 참조하고

같은 질문 ("Field-like events")에서 연결된 MSDN 기사 추가 :

는 이벤트를 발생의 개념이 정확하게 상응하는

입니다 이벤트에 의해 표현 된 대표단을 호출하기 위해 - 과 같이 이벤트를 발생시키는 특수 언어가 없습니다. . 추가 검사 싶은

, 나는 이벤트 및 대리자로 컴파일하는 IL를보기 위해 테스트 프로젝트를 구축 : 내가 기대

public class TestClass 
{ 
    public EventHandler handler; 
    public event EventHandler FooEvent; 

    public TestClass() 
    { } 
} 

대리자 필드 handler 이벤트 FooEvent에 컴파일러에서 생성 된 FooEvent 필드에 대한 액세스를 래핑하는 몇 가지 추가 메서드를 사용하여 거의 동일한 IL 코드로 컴파일하십시오. 그러나 IL 내가 기대했던 아주 아니었다 생성 :

.class public auto ansi beforefieldinit TestClass 
    extends [mscorlib]System.Object 
{ 
    .event [mscorlib]System.EventHandler FooEvent 
    { 
     .addon instance void TestClass::add_FooEvent(class [mscorlib]System.EventHandler) 
     .removeon instance void TestClass::remove_FooEvent(class [mscorlib]System.EventHandler) 
    } 

    .method public hidebysig specialname rtspecialname instance void .ctor() cil managed 
    { 
     // Constructor IL hidden 
    } 

    .field private class [mscorlib]System.EventHandler FooEvent 
    .field public class [mscorlib]System.EventHandler handler 
} 

이벤트가 컴파일러가 생성 addremove 방법과 대표에 지나지 않기 때문에, 나는에서 그 이상 아무것도 처리 이벤트를 볼 기대하지 않았다 IL. 그러나 추가 및 제거 방법은 .event으로 시작하는 절에서 정의되며, 일반적인 방법으로는 .method이 아닙니다.

궁극적 인 질문 : 이벤트가 접근 자 메서드를 사용하는 대리자로 구현되는 경우 .event 일리노이 섹션의 요점은 무엇입니까? .method 섹션을 사용하여 이것없이 일리노이에서 구현할 수 없었습니까? .event.method과 같습니까? 나는 그 필드 대 속성에 대해 동일한 비교 ... 놀라운 확실하지 않다

답변

6

(이벤트와 동일한 기능 이전 등록 이후 : 접근을 통해 캡슐화) : 또한

.field public string Foo // public field 
.property instance string Bar // public property 
{ 
    .get instance string MyType::get_Bar() 
    .set instance void MyType::set_Bar(string) 
} 

- 이벤트 할 아니 필드에 대해 언급 아무것도; 그들은 접근자를 정의 (추가/제거)합니다. 위임자 후원자는 구현 세부 사항입니다. 자동 구현 된 속성이 필드를지지 멤버로 선언하는 것과 같은 방법으로 필드와 같은 이벤트가지지 멤버로 필드를 선언하는 경우가 있습니다. 다른 구현도 가능합니다 (특히 양식 등에서는 매우 일반적입니다).

다른 일반적인 구현 :

스파 스 - 이벤트 (제어, 등) - EventHandlerList (또는 유사) :

// only one instance field no matter how many events; 
// very useful if we expect most events to be unsubscribed 
private EventHandlerList events = new EventHandlerList(); 
protected EventHandlerList Events { 
    get { return events; } // usually lazy 
} 

// this code repeated per event 
private static readonly object FooEvent = new object(); 
public event EventHandler Foo 
{ 
    add { Events.AddHandler(FooEvent, value); } 
    remove { Events.RemoveHandler(FooEvent, value); } 
} 
protected virtual void OnFoo() 
{ 
    EventHandler handler = Events[FooEvent] as EventHandler; 
    if (handler != null) handler(this, EventArgs.Empty); 
} 

(위가 윈 - 형태의 이벤트 꽤-많은 백본)

외관 (이것은 "보낸 사람"조금 혼란 있지만 일부 중간 코드는 종종 도움이된다) :

private Bar wrappedObject; // via ctor 
public event EventHandler SomeEvent 
{ 
    add { wrappedObject.SomeOtherEvent += value; } 
    remove { wrappedObject.SomeOtherEvent -= value; } 
} 
,515,

+0

당신이 일반적인 다른 구현에 대한 몇 가지 자세한 정보를 줄 수 있습니까? 난 당신이 다른 구현 구현에 대해 이야기하고 있다고 가정합니다. 다른 멤버가 아니라 다른 멤버가 아닌 다른 멤버를 대신 멤버로 필드를 사용하는 것입니다. – Chris

+0

감사합니다. 대리자 필드 대신 EventHandlerList를 사용하여 구현 된 이벤트를지지 멤버로 지정하려면 어떻게해야합니까? 'event' 키워드의 기본 동작이 델리게이트 필드를 사용하는 것이라면 C#을 우회하여 직접 IL을 작성해야합니까? – Chris

+1

나는 이미 위에 썼다. add */remove *를 지정하지 않은 경우 기본 *는 위임 필드를 사용하는 것입니다. 추가/제거를 지정하면 (위에서처럼) 원하는 것을 할 수 있습니다. 아마도 postsharp를 통해이 작업을 수행 할 수있을 것입니다,하지만 난 당신이 아마 일을 단순화하기 위해 IDE에서 "미리보기"를 사용할 수 –

2

이벤트 대리자 같은 아니다 (상기도 효과적으로 이벤트의 이름을 변경하는데 사용될 수있다). 이벤트는 이벤트에 대한 핸들러 추가/제거를 캡슐화합니다. 핸들러는 델리게이트로 표현됩니다.

등 모든 이벤트에 대해 AddClickHandler/RemoveClickHandler 등을 작성하면 비교적 어려울 수 있지만 VS와 같은 도구가 다른 이벤트를 쉽게 구분할 수는 없습니다.

이것은 GetSize/SetSize 등 (자바에서와 같이)을 작성할 수 있지만 속성을 구분하여 사용할 수있는 구문 단축키와 더 나은 도구 지원이 있습니다.

+0

당신은 할 수 있습니다 ... 내가 귀찮게 거라고 확실하지 않다 대표와 같은 일이, 그들은 + = –

+1

(null의 대표와) 결합을 지원하지만 그들은 * 단지 * 지원 + =을하지 않습니다 - 그게 포인트입니다. –

+1

물론 단점은 리팩토링하거나 이벤트로 상위 레벨 코드를 작성할 수 없다는 것입니다. 은 "비동기 이벤트 기반"물건처럼 MS가 지금에 너무 뜨거워 - 당신은 이벤트 대리자를 참조 할 수 없습니다와 그 유형에 따라 전달받을 때문에, 일반적으로 그것을 코드에 통증이 ... – MichaelGG

1

추가, 제거, 메소드의 쌍인 이벤트가있는 지점은 캡슐화입니다. 시간 이벤트의 대부분

은 그대로 사용되지만, 다른 시간 당신은 필드에 이벤트에 부착 된 대표를 저장하지 않는, 또는 당신은 추가에 추가 처리을하거나 이벤트 방법을 제거 할.

예를 들어 memory efficient events을 구현하는 한 가지 방법은 항목이 추가 될 때 사전 크기가 커지면서 필드가 항상 할당되므로 대리자를 개인 필드가 아닌 사전에 저장하는 것입니다. 이 모델은 윈폼과 WPF의 메모리를 효율적으로 사용할 수 있도록 만든다 사용하는 것과 유사하다 (윈폼 및 WPF에는 키가 사전 목록이 없습니다 대의원을 저장하는 데 사용)

+0

IMHO, 이벤트가 더 나은 것 remove 메소드가 없다면 캡슐화됩니다. 단지 "add"메소드를 리턴 한 MethodInvoker를 호출 할 때 이벤트를 제거합니다.그러면 Remove 처리기가 Add 처리기에서 임의의 정보를받을 수 있습니다. 예를 들어 구독이 연결된 목록을 통해 관리되는 경우 대리자 만 주어진 remove 메서드에서 특정 구독을 찾는 경우 선형 검색이 필요하지만 MethodInvoker를 통해 제거를 수행하면 대리자가 적절한 목록 노드에 대한 참조를 포함 할 수 있습니다. – supercat

+0

@supercat, 더 좋은 방법 이었지만 더 비싼 방법이었을 것입니다. 대의원은 꽤 많은 메모리 소비자입니다. 귀하의 방법은 각 aded 이벤트에 대해 2 명의 위임자를 가지므로 이벤트 모델에 대한 메모리 사용량을 최소한 두 배로 늘릴 수 있습니다. 이벤트 팽창으로 인한 메모리 소비는 .Net 응용 프로그램의 실제 문제이므로 그대로두면 더 큰 문제가 될 필요가 없습니다. –

+0

@Pop : 대리인이 너무 많은 메모리를 사용하면 IUnsubscribe를 구현하는 개체를 반환 할 수 있습니다.이 개체의 한 가지 방법은 이벤트를 구독 취소합니다. 연결된 목록 또는 이벤트를 보유하는 다른 방법을 사용할지 여부는 호출 속도, 가입 속도 및 가입 취소 속도 사이의 균형입니다. 이벤트 구독 취소를 수행하는 객체를 갖는 한 가지 이점은 그러한 객체의 종료자가 이벤트 구독을 취소하는 데 사용할 수 있다는 것입니다. 이벤트 위임자가 finalizer가 보관 된 객체를 가리키는 경우이 작업은 작동하지 않지만 – supercat

관련 문제