C #에서 이벤트 구독을 지우려면 어떻게해야합니까?


141

다음 C # 클래스를 보자.

c1 {
 event EventHandler someEvent;
}

c1someEvent이벤트에 대한 구독이 많고 모두 지우고 싶은 경우이를 달성하는 가장 좋은 방법은 무엇입니까? 또한이 이벤트 구독은 람다 / 익명 대리인 일 수 있습니다.

현재 내 솔루션은 해당 세트 에 ResetSubscriptions()메소드 를 null 로 추가하는 것 입니다. 이것이 보이지 않는 결과가 있는지는 모르겠습니다.c1someEvent

답변:


181

클래스 내에서 (숨겨진) 변수를 null로 설정할 수 있습니다. null 참조는 효과적으로 빈 호출 목록을 나타내는 일반적인 방법입니다.

클래스 외부에서는이 작업을 수행 할 수 없습니다. 이벤트에는 기본적으로 "구독"과 "구독 취소"가 표시됩니다.

필드와 유사한 이벤트가 실제로 수행하는 작업을 인식하는 것이 좋습니다. 변수 이벤트를 동시에 만듭니다. 클래스 내에서 변수를 참조하게됩니다. 외부에서는 이벤트를 참조합니다.

자세한 내용은 이벤트 및 대리인에 대한 내 기사 를 참조하십시오.


3
당신이 고집이 있다면, 당신은 반사를 통해 그것을 명확하게 할 수 있습니다. stackoverflow.com/questions/91778/…를 참조하십시오 .
Brian

1
@Brian : 구현에 따라 다릅니다. 그것의 경우 단지 A 이벤트 또는 현장처럼 EventHandlerList, 당신은 할 수 있습니다. 그래도이 두 가지 경우를 인식해야합니다. 다른 구현도있을 수 있습니다.
Jon Skeet

@Joshua : 아니요. 변수 값이 null이되도록 설정합니다. 변수가 호출되지 않을 것에 동의합니다 hidden.
Jon Skeet

@JonSkeet 그것이 내가 생각한 것입니다. 그것이 쓰여진 방식은 5 분 동안 혼란 스러웠다.

@JoshuaLamusga : 기존 객체를 수정하는 것처럼 들리는 호출 목록을 지우겠다고 말했습니다.
Jon Skeet

34

'someEvent'를 null로 설정하는 메소드를 c1에 추가하십시오.

public class c1
{
    event EventHandler someEvent;
    public ResetSubscriptions() => someEvent = null;    
}

이것이 내가보고있는 행동입니다. 내 질문에서 말했듯이, 내가 뭔가를 간과하는지 모르겠습니다.
프로그래머

8
class c1
{
    event EventHandler someEvent;
    ResetSubscriptions() => someEvent = delegate { };
}

null ref 예외를 피하는 delegate { }것보다 사용하는 것이 좋습니다 null.


2
왜? 이 답변을 확장 해 주시겠습니까?
S. Buda

1
@ S.Buda null 인 경우 null 참조가 표시되므로 List.Clear()vs를 사용하는 것과 같습니다 myList = null.
AustinWBryan

6

클래스 내에서 이벤트를 null로 설정하면 작동합니다. 클래스를 처리 할 때 항상 이벤트를 null로 설정해야합니다. GC는 이벤트에 문제가 있으며 매달려있는 이벤트가있는 경우 처리 된 클래스를 정리하지 못할 수 있습니다.


6

모든 구독자를 지우는 가장 좋은 방법은이 기능을 외부에 노출하려는 경우 다른 공용 메소드를 추가하여 someEvent를 null로 설정하는 것입니다. 이것은 보이지 않는 결과가 없습니다. 전제 조건은 키워드 'event'를 사용하여 SomeEvent를 선언하는 것입니다.

요컨대, 125 페이지, 책 C # 4.0을 참조하십시오.

여기서 일부는 Delegate.RemoveAll방법 사용을 제안했습니다 . 사용하면 샘플 코드가 아래 양식을 따를 수 있습니다. 그러나 정말 바보입니다. 왜 함수 SomeEvent=null내부에 있지 ClearSubscribers()않습니까?

public void ClearSubscribers ()
{
   SomeEvent = (EventHandler) Delegate.RemoveAll(SomeEvent, SomeEvent);
   // Then you will find SomeEvent is set to null.
}

5

Delegate.Remove 또는 Delegate.RemoveAll 메소드를 사용하여이를 달성 할 수 있습니다.


6
나는 이것이 람다 표현이나 익명의 델리게이트와 함께 작동한다고 생각하지 않습니다.
프로그래머

3

개념적 확장 된 지루한 의견.

"event"또는 "delegate"대신 "event handler"라는 단어를 사용합니다. 그리고 다른 것들에 "이벤트"라는 단어를 사용했습니다. 일부 프로그래밍 언어 (VB.NET, Object Pascal, Objective-C)에서 "이벤트"는 "메시지"또는 "신호"라고하며 "메시지"키워드와 특정 설탕 구문이 있습니다.

const
  WM_Paint = 998;  // <-- "question" can be done by several talkers
  WM_Clear = 546;

type
  MyWindowClass = class(Window)
    procedure NotEventHandlerMethod_1;
    procedure NotEventHandlerMethod_17;

    procedure DoPaintEventHandler; message WM_Paint; // <-- "answer" by this listener
    procedure DoClearEventHandler; message WM_Clear;
  end;

또한 "메시지"에 응답하기 위해 "대리인"은 단일 대리인이든 여러 대리인이든 응답합니다.

요약 : "이벤트"는 "질문", "이벤트 핸들러"는 답입니다.


1

이것은 내 솔루션입니다.

public class Foo : IDisposable
{
    private event EventHandler _statusChanged;
    public event EventHandler StatusChanged
    {
        add
        {
            _statusChanged += value;
        }
        remove
        {
            _statusChanged -= value;
        }
    }

    public void Dispose()
    {
        _statusChanged = null;
    }
}

모든 호출 목록 멤버를 등록 해제 Dispose()하려면 using(new Foo()){/*...*/}패턴 을 호출 하거나 사용해야 합니다.


0

모든 이벤트를 제거하십시오. 이벤트가 "액션"유형이라고 가정하십시오.

Delegate[] dary = TermCheckScore.GetInvocationList();

if ( dary != null )
{
    foreach ( Delegate del in dary )
    {
        TermCheckScore -= ( Action ) del;
    }
}

1
이벤트를 선언 한 유형의 내부에있는 경우이 작업을 수행 할 필요가 없습니다.이 유형을 벗어난 경우 null로 설정하면 델리게이트의 호출 목록을 얻을 수 없습니다. 또한 호출시 이벤트가 null 인 경우 코드에서 예외가 발생합니다 GetInvocationList.
Servy

-1

콜백을 수동으로 추가 및 제거하는 대신 여러 곳에서 여러 델리게이트 유형을 선언하는 대신 :

// The hard way
public delegate void ObjectCallback(ObjectType broadcaster);

public class Object
{
    public event ObjectCallback m_ObjectCallback;
    
    void SetupListener()
    {
        ObjectCallback callback = null;
        callback = (ObjectType broadcaster) =>
        {
            // one time logic here
            broadcaster.m_ObjectCallback -= callback;
        };
        m_ObjectCallback += callback;

    }
    
    void BroadcastEvent()
    {
        m_ObjectCallback?.Invoke(this);
    }
}

이 일반적인 접근 방식을 시도해 볼 수 있습니다.

public class Object
{
    public Broadcast<Object> m_EventToBroadcast = new Broadcast<Object>();

    void SetupListener()
    {
        m_EventToBroadcast.SubscribeOnce((ObjectType broadcaster) => {
            // one time logic here
        });
    }

    ~Object()
    {
        m_EventToBroadcast.Dispose();
        m_EventToBroadcast = null;
    }

    void BroadcastEvent()
    {
        m_EventToBroadcast.Broadcast(this);
    }
}


public delegate void ObjectDelegate<T>(T broadcaster);
public class Broadcast<T> : IDisposable
{
    private event ObjectDelegate<T> m_Event;
    private List<ObjectDelegate<T>> m_SingleSubscribers = new List<ObjectDelegate<T>>();

    ~Broadcast()
    {
        Dispose();
    }

    public void Dispose()
    {
        Clear();
        System.GC.SuppressFinalize(this);
    }

    public void Clear()
    {
        m_SingleSubscribers.Clear();
        m_Event = delegate { };
    }

    // add a one shot to this delegate that is removed after first broadcast
    public void SubscribeOnce(ObjectDelegate<T> del)
    {
        m_Event += del;
        m_SingleSubscribers.Add(del);
    }

    // add a recurring delegate that gets called each time
    public void Subscribe(ObjectDelegate<T> del)
    {
        m_Event += del;
    }

    public void Unsubscribe(ObjectDelegate<T> del)
    {
        m_Event -= del;
    }

    public void Broadcast(T broadcaster)
    {
        m_Event?.Invoke(broadcaster);
        for (int i = 0; i < m_SingleSubscribers.Count; ++i)
        {
            Unsubscribe(m_SingleSubscribers[i]);
        }
        m_SingleSubscribers.Clear();
    }
}

질문을 형식화하고 왼쪽의 공백을 모두 제거 할 수 있습니까? IDE에서 복사하여 붙여 넣을 때 이런 일이 발생할 수 있습니다.
AustinWBryan

방금 그 공백을 없애
버렸어
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.