다음을 통해 공유


Windows 런타임 구성 요소의 사용자 지정 이벤트 및 이벤트 접근자

Windows 런타임 구성 요소에 대한 .NET 지원을 사용하면 UWP(유니버설 Windows 플랫폼) 이벤트 패턴과 .NET 이벤트 패턴 간의 차이점을 숨겨 이벤트 구성 요소를 쉽게 선언할 수 있습니다. 그러나 Windows 런타임 구성 요소에서 사용자 지정 이벤트 접근자를 선언할 때는 UWP에서 사용되는 패턴을 따라야 합니다.

이벤트 등록하기

UWP에서 이벤트를 처리하도록 등록하면 추가 접근자가 토큰을 반환합니다. 등록을 취소하려면, 제거 접근자에게 해당 토큰을 전달합니다. 즉, UWP 이벤트에 대한 추가 및 제거 접근자는 익숙한 접근자와 시그니처가 다릅니다.

Visual Basic 및 C# 컴파일러를 사용하면 이 프로세스를 간단히 수행할 수 있습니다. Windows 런타임 구성 요소에서 사용자 지정 접근자로 이벤트를 선언할 때 컴파일러에서 자동으로 UWP 패턴을 사용합니다. 예를 들어 추가 접근자가 토큰을 반환하지 않으면 컴파일러 오류가 발생합니다. .NET은 구현을 지원하는 다음 두 가지 형식을 제공합니다.

  • 해당 EventRegistrationToken 구조체는 토큰을 나타냅니다.
  • 해당 EventRegistrationTokenTable<T> 클래스는 토큰을 만들고 토큰과 이벤트 처리기 간의 매핑을 유지합니다. 제네릭 타입 인수는 이벤트 인수 타입입니다. 각 이벤트에 대해 이벤트 처리기를 처음으로 등록할 때 각 이벤트별 클래스의 인스턴스를 만듭니다.

NumberChanged 이벤트에 대한 다음 코드는 UWP 이벤트에 대한 기본 패턴을 보여 줍니다. 이 예제에서 이벤트 인수 객체의 생성자인 NumberChangedEventArgs는 변경된 숫자 값을 나타내는 단일 정수 매개 변수를 사용합니다.

참고 이는 Windows 런타임 구성 요소에서 선언된 일반 이벤트에 대해 컴파일러를 사용할 때와 동일한 패턴입니다.

 

private EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
    m_NumberChangedTokenTable = null;

public event EventHandler<NumberChangedEventArgs> NumberChanged
{
    add
    {
        return EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
            .GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
            .AddEventHandler(value);
    }
    remove
    {
        EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
            .GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
            .RemoveEventHandler(value);
    }
}

internal void OnNumberChanged(int newValue)
{
    EventHandler<NumberChangedEventArgs> temp =
        EventRegistrationTokenTable<EventHandler<NumberChangedEventArgs>>
        .GetOrCreateEventRegistrationTokenTable(ref m_NumberChangedTokenTable)
        .InvocationList;
    if (temp != null)
    {
        temp(this, new NumberChangedEventArgs(newValue));
    }
}
Private m_NumberChangedTokenTable As  _
    EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs))

Public Custom Event NumberChanged As EventHandler(Of NumberChangedEventArgs)

    AddHandler(ByVal handler As EventHandler(Of NumberChangedEventArgs))
        Return EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
            GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
            AddEventHandler(handler)
    End AddHandler

    RemoveHandler(ByVal token As EventRegistrationToken)
        EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
            GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
            RemoveEventHandler(token)
    End RemoveHandler

    RaiseEvent(ByVal sender As Class1, ByVal args As NumberChangedEventArgs)
        Dim temp As EventHandler(Of NumberChangedEventArgs) = _
            EventRegistrationTokenTable(Of EventHandler(Of NumberChangedEventArgs)).
            GetOrCreateEventRegistrationTokenTable(m_NumberChangedTokenTable).
            InvocationList
        If temp IsNot Nothing Then
            temp(sender, args)
        End If
    End RaiseEvent
End Event

정적(Visual Basic의 Shared) GetOrCreateEventRegistrationTokenTable 메서드는 나중에 EventRegistrationTokenTable<T> 개체의 이벤트 인스턴스를 만듭니다. 토큰 테이블 인스턴스를 보유할 클래스 수준 필드를 이 메서드에 전달합니다. 필드가 비어 있으면 메서드로 테이블을 만들고, 테이블에 대한 참조를 필드에 저장하고, 테이블에 대한 참조를 반환합니다. 필드에 토큰 테이블 참조가 이미 있는 경우 메서드는 해당 참조만 반환합니다.

중요 스레드 보안을 유지하기 위해 EventRegistrationTokenTable<T> 이벤트 인스턴스를 포함하는 필드는 클래스 수준 필드여야 합니다. 클래스 수준 필드의 경우 GetOrCreateEventRegistrationTokenTable 메서드는 여러 스레드가 토큰 테이블을 만들려고 할 때 모든 스레드가 테이블의 동일한 인스턴스를 가져오도록 합니다. 지정된 이벤트의 경우 GetOrCreateEventRegistrationTokenTable 메서드에 대한 모든 호출은 동일한 클래스 수준 필드를 사용해야 합니다.

제거 접근자 및 RaiseEvent 메서드(C#의 OnRaiseEvent)에서 GetOrCreateEventRegistrationTokenTable 메서드를 호출하면 이벤트 처리기 대리자가 추가되기 전에 해당 메서드를 호출하는 경우, 예외가 발생하지 않습니다.

UWP 이벤트 패턴에서 사용되는 EventRegistrationTokenTable<T> 클래스의 다른 멤버는 다음과 같습니다.

  • AddEventHandler 메서드는 이벤트 처리기 대리자의 토큰을 생성하고, 대리자를 테이블에 저장하고, 호출 목록에 추가하고, 토큰을 반환합니다.

  • RemoveEventHandler(EventRegistrationToken) 메서드 오버로드는 테이블 및 호출 목록에서 대리자를 제거합니다.

    참고: AddEventHandler 및 RemoveEventHandler(EventRegistrationToken) 메서드는 스레드 안전을 보장하기 위해 테이블을 잠급니다.

  • 호출 리스트 속성은 이벤트 처리를 위해 현재 등록된 모든 이벤트 처리기를 포함한 대리자를 반환합니다. 이 대리자를 사용하여 이벤트를 발생하거나 Delegate 클래스의 메서드를 사용하여 처리기를 개별적으로 호출합니다.

    참고 이 문서의 앞부분에서 제공된 예제에 표시된 패턴을 따르고 대리자를 호출하기 전에 임시 변수에 대리자를 복사하는 것을 추천합니다. 이렇게 하면 한 스레드가 마지막 처리기를 제거하는 경합 상태를 방지하여 다른 스레드가 대리자를 호출하기 직전에 대리자를 null값으로 축소합니다. 대리자는 변경할 수 없으므로 복사본은 여전히 유효합니다.

접근자에 고유 코드를 적절히 배치합니다. 스레드 보안 문제가 있는 경우 사용자 스스로가 코드에 잠금 기법을 적용해야 합니다.

C# 사용자: UWP 이벤트 패턴에서 사용자 지정 이벤트 접근자를 작성할 때 컴파일러는 일반적인 구문 바로 가기를 제공하지 않습니다. 코드에서 이벤트 이름을 사용하는 경우 오류가 발생합니다.

Visual Basic 사용자: .NET에서 이벤트는 등록된 모든 이벤트 처리기를 나타내는 멀티캐스트 대리자입니다. 이벤트 발생은 대리자 호출을 의미합니다. Visual Basic 구문은 일반적으로 대리자와의 상호 작용을 숨기고 컴파일러는 스레드 보안에 대한 메모에 설명된 대로 대리자를 호출하기 전에 대리자를 복사합니다. Windows 런타임 구성 요소에서 사용자 지정 이벤트를 만들 때 대리자를 직접 처리해야 합니다. 예를 들어 처리기를 별도로 호출하려는 경우 MulticastDelegate.GenInovcationList 메서드를 사용하여 각 이벤트 처리기를 위한 개별 대리자가 포함된 배열을 불러올 수도 있습니다.