이벤트 핸들러가 이미 추가 되었습니까?


183

이벤트 핸들러가 객체에 추가되었는지 알 수있는 방법이 있습니까? SQL 기반 세션 상태를 사용할 수 있도록 객체 목록을 세션 상태로 / 외부에서 직렬화하고 있습니다 ... 목록의 객체에 속성이 변경되면 플래그를 지정해야합니다. . 그러나 이제 객체가 직렬화 해제되면 이벤트 핸들러가 표시되지 않습니다.

약간의 불편 함을 느끼기 위해 방금 객체에 액세스하는 Get 속성에 이벤트 핸들러를 추가했습니다. 5 번처럼 호출되는 것을 제외하고는 지금 훌륭하게 호출됩니다. 따라서 객체에 액세스 할 때마다 핸들러가 계속 추가된다고 생각합니다.

그냥 무시할 정도로 안전하지만 처리기가 이미 추가되었는지 확인하여 한 번만 수행하면 훨씬 더 깨끗하게 만들 수 있습니다.

가능합니까?

편집 : 이벤트 처리기가 추가되는 것을 완전히 제어 할 필요는 없으므로 null을 확인하는 것만으로는 충분하지 않습니다.


답변:


123

@Telos가 언급했듯이 정의 클래스 외부에서 a +=또는 a 의 왼쪽에서만 EventHandler를 사용할 수 있습니다 -=. 따라서 정의 클래스를 수정하는 기능이 있으면 이벤트 핸들러가 있는지 확인하여 점검을 수행하는 메소드를 제공 할 수 있습니다. null그렇다면 이벤트 핸들러가 추가되지 않은 것입니다. 그렇지 않으면 아마도 Delegate.GetInvocationList 의 값을 반복 할 수 있습니다 . 하나가 이벤트 핸들러로 추가하려는 대리자와 같으면 거기에 있음을 알 수 있습니다.

public bool IsEventHandlerRegistered(Delegate prospectiveHandler)
{   
    if ( this.EventHandler != null )
    {
        foreach ( Delegate existingHandler in this.EventHandler.GetInvocationList() )
        {
            if ( existingHandler == prospectiveHandler )
            {
                return true;
            }
        }
    }
    return false;
}

그리고 "핸들러가없는 경우 추가"가되도록 쉽게 수정할 수 있습니다. 당신이 이벤트를 노출하는 것 클래스의 내장에 액세스 할 수없는 경우, 당신은 탐구해야 -=하고 +=@Lou 프랑코에 의해 제안.

그러나 이러한 정보를 직접 추적하는 방법을 찾을 수 없는지 확인하기 위해 이러한 오브젝트를 커미셔닝 및 해제하는 방법을 다시 검토하는 것이 좋습니다.


7
컴파일되지 않으며 EventHandler는 + = 또는-=의 왼쪽에만있을 수 있습니다.
CodeRedick

2
추가 설명으로 투표가 삭제되었습니다. SQL 상태는 여기에서 전체 아이디어를 거의 파괴하고 있습니다 ... :(
CodeRedick

1
고마워 블레어와 SO 검색, 내가 찾던 것 (클래스 밖에서 할 수없는 성가신)
George Mauer

3
대의자를 동등하게 비교하는 동안 대부분의 경우 문제가 발생합니다. 따라서 Delegate.Equals(objA, objB)동일한 델리게이트 존재를 확인하려면 사용 하십시오. 그렇지 않으면 속성을 개별적으로 비교하십시오 if(objA.Method.Name == objB.Method.Name && objA.Target.GetType().FullName == objB.Target.GetType().FullName).
Sanjay

2
이 코드는 WinForm에서 작동하지 않습니다. ASP.NET에만 해당됩니까?
jp2code

212

최근에 이벤트 처리기를 한 번만 등록 해야하는 비슷한 상황이 발생했습니다. 핸들러가 전혀 등록되지 않은 경우에도 먼저 안전하게 등록을 취소 한 다음 다시 등록 할 수 있습니다.

myClass.MyEvent -= MyHandler;
myClass.MyEvent += MyHandler;

핸들러를 등록 할 때마다이 작업을 수행하면 핸들러가 한 번만 등록됩니다. 나에게 꽤 좋은 연습처럼 들린다 :)


9
위험 해 보인다; 핸들러를 제거한 후 다시 추가하기 전에 이벤트가 발생하면 누락됩니다.
Jimmy

27
확실한. 스레드 안전하지 않다는 것을 의미합니다. 그러나 다중 스레드 등을 실행할 때만 문제가 될 수 있습니다. 대부분의 경우 이는 단순성을 위해 충분해야합니다.
alf

6
이것은 나를 귀찮게합니다. 현재 코드에서 스레드를 명시 적으로 생성하지 않았다고해서 여러 스레드가 없거나 나중에 추가되지 않는다는 의미는 아닙니다. 몇 달 후 또는 팀의 다른 사람이 워커 스레드를 추가하거나 UI 및 네트워크 연결에 모두 응답하자마자 간헐적으로 끊어진 이벤트가 발생할 수 있습니다.
Technophile

1
제거하고 다시 추가하는 데 걸리는 시간이 너무 짧아서 누락 된 이벤트가있을 가능성은 거의 없지만 그래도 여전히 가능합니다.
Alisson

2
UI 등을 업데이트하는 데 이것을 사용하면 사소한 작업이 위험합니다. 이것이 네트워크 패킷 처리 등을위한 것이라면 사용하지 않을 것입니다.

18

이것이 유일한 핸들러 인 경우 이벤트가 null인지 확인하고 그렇지 않은 경우 핸들러가 추가되었는지 확인할 수 있습니다.

처리기가 추가되지 않은 경우에도 처리기를 사용하여 이벤트에서-=을 안전하게 호출 할 수 있다고 생각합니다. 추가하지 않으면 잡을 수 없습니다.


3
이 논리는 이벤트가 다른 곳에서도 처리되는 즉시 중단됩니다.
bugged87

6

이 예제는 GetInvocationList () 메소드를 사용하여 추가 된 모든 핸들러에 대한 델리게이트를 검색하는 방법을 보여줍니다. 특정 핸들러 (함수)가 추가되었는지 확인하려면 배열을 사용할 수 있습니다.

public class MyClass
{
  event Action MyEvent;
}

...

MyClass myClass = new MyClass();
myClass.MyEvent += SomeFunction;

...

Action[] handlers = myClass.MyEvent.GetInvocationList(); //this will be an array of 1 in this example

Console.WriteLine(handlers[0].Method.Name);//prints the name of the method

델리게이트의 Method 속성에서 다양한 속성을 검사하여 특정 함수가 추가되었는지 확인할 수 있습니다.

하나만 첨부되어 있는지 확인하려면 null을 테스트하면됩니다.


GetInvocationList ()는 내 클래스의 멤버가 아닙니다. 실제로, 내가 액세스 할 수있는 객체 나 핸들러에서이 메소드를 찾을 수없는 것 같습니다.
CodeRedick

나는 그것을 시도했지만, 분명히 당신은 클래스 내부에서만 그런 이벤트에 액세스 할 수 있습니다. 나는 이것을 일반적으로하고 있으며, 다른 사람들이 언급했듯이 이벤트 핸들러는 어쨌든 길을 잃을 것입니다. 설명 주셔서 감사합니다!
CodeRedick

4

문제를 올바르게 이해하면 더 큰 문제가있을 수 있습니다. 다른 객체가이 이벤트를 구독 할 수 있다고 말했습니다. 객체가 직렬화되고 역 직렬화되면 다른 객체 (제어 할 수없는 객체)의 이벤트 핸들러가 손실됩니다.

걱정하지 않는다면 이벤트 핸들러에 대한 참조를 유지하는 것이 좋습니다. 이벤트 핸들러를 잃은 다른 오브젝트의 부작용에 대해 걱정이된다면 캐싱 전략을 다시 생각할 수 있습니다.


1
도! 내 생각은 내 자신의 핸들러가 길을 잃었다는 것을 알았지 만 분명히 생각해야했지만.
CodeRedick

2

나를 위해 일한 유일한 방법은 이벤트를 추가 할 때 true로 설정 한 부울 변수를 만드는 것입니다. 그런 다음 묻습니다. 변수가 false이면 이벤트를 추가합니다.

bool alreadyAdded = false;

이 변수는 전역적일 수 있습니다.

if(!alreadyAdded)
{
    myClass.MyEvent += MyHandler;
    alreadyAdded = true;
}

1

나는 alf의 답변에 동의하지만 그것에 약간의 수정은 사용하는 것입니다.

           try
            {
                control_name.Click -= event_Click;
                main_browser.Document.Click += Document_Click;
            }
            catch(Exception exce)
            {
                main_browser.Document.Click += Document_Click;
            }

0
EventHandler.GetInvocationList().Length > 0

2
list == null 일 때 이것이 발생하지 않습니까?
Boris Callens

2
이벤트 핸들러를 소유 한 클래스 외부에서는-= 및 + = 만 사용할 수 있습니다. 이벤트에 액세스 할 수 없습니다.
tbergelt
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.