Windows 런타임 구성 요소의 사용자 지정 이벤트 및 이벤트 접근자
Windows 런타임에 대한 .NET Framework 지원 기능은 Windows 런타임 구성 요소에서 이벤트를 쉽게 선언할 수 있도록 Windows 런타임 이벤트 패턴과 .NET Framework 이벤트 패턴 간 차이를 숨깁니다. 하지만 Windows 런타임 구성 요소에서 사용자 지정 이벤트 접근자를 선언하는 경우 Windows 런타임 패턴을 따라야 합니다.
Windows 런타임에서 이벤트를 처리하도록 등록하면 add 접근자는 토큰을 반환합니다. 등록을 취소하려면 이 토큰을 remove 접근자에 전달합니다. 즉 Windows 런타임 이벤트의 add 및 remove 접근자는 그동안 사용해 온 접근자와는 다른 시그니처를 가지고 있습니다.
다행히 Visual Basic 및 C# 컴파일러는 이 프로세스를 간소화합니다. 즉, Windows 런타임 구성 요소에서 사용자 지정 접근자가 있는 이벤트를 선언하면 컴파일러는 자동으로 Windows 런타임 패턴을 사용합니다. 예를 들어 add 접근자가 토큰을 반환하지 않으면 컴파일러 오류가 발생합니다. .NET Framework는 이 구현을 지원하기 위해 다음과 같은 두 가지 형식을 제공합니다.
EventRegistrationToken 구조체는 토큰을 나타냅니다.
EventRegistrationTokenTable<T> 클래스는 토큰을 만들고 토큰과 이벤트 처리기 간의 매핑을 유지합니다. 제네릭 형식 인수는 이벤트 인수 형식입니다. 해당 이벤트에 대해 이벤트 처리기가 처음 등록되면 각 이벤트에 대해 이 클래스의 인스턴스를 만듭니다.
NumberChanged 이벤트에 대한 다음 코드는 Windows 런타임 이벤트의 기본 패턴을 보여 줍니다. 이 예제에서 이벤트 인수 개체의 생성자 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
static(Visual Basic에서는 Shared) GetOrCreateEventRegistrationTokenTable 메서드는 EventRegistrationTokenTable<T> 개체의 이벤트 인스턴스 생성을 최대한 지연합니다. 토큰 테이블 인스턴스를 보유하는 클래스 수준 필드를 이 메서드에 전달하세요. 필드가 비어 있으면 메서드는 테이블을 만들고, 필드에 해당 테이블에 대한 참조를 저장하고, 테이블에 대한 참조를 반환합니다. 필드에 이미 토큰 테이블 참조가 있는 경우 메서드는 즉시 해당 참조를 반환합니다.
중요
스레드 안정성을 보장하기 위해 EventRegistrationTokenTable<T>의 이벤트 인스턴스를 보유하는 필드는 클래스 수준 필드여야 합니다. 클래스 수준 필드인 경우 GetOrCreateEventRegistrationTokenTable 메서드는 여러 스레드가 토큰 테이블을 만들려고 할 때 모든 스레드가 동일한 테이블 인스턴스를 가져가도록 보장합니다. 지정된 이벤트에 대해 GetOrCreateEventRegistrationTokenTable 메서드에 대한 모든 호출은 동일한 클래스 수준 필드를 사용해야 합니다.
remove 접근자 및 RaiseEvent 메서드(C#에서는 OnRaiseEvent 메서드)에서 GetOrCreateEventRegistrationTokenTable 메서드를 호출하면 이벤트 처리기 대리자가 추가되기 전에 이 메서드가 호출되는 한 예외가 발생하지 않습니다.
Windows 런타임 이벤트 패턴에서 사용되는 EventRegistrationTokenTable<T> 클래스의 다른 멤버는 다음과 같습니다.
AddEventHandler 메서드는 이벤트 처리기 대리자에 대한 토큰을 생성하고, 테이블에 대리자를 저장하고, 호출 목록에 대리자를 추가하며, 토큰을 반환합니다.
RemoveEventHandler(EventRegistrationToken) 메서드 오버로드는 테이블과 호출 목록에서 대리자를 제거합니다.
참고
AddEventHandler 및 RemoveEventHandler(EventRegistrationToken) 메서드는 스레드 안정성을 보장하기 위해 테이블을 잠급니다.
InvocationList 속성은 현재 이벤트를 처리하도록 등록된 모든 이벤트 처리기를 포함하는 대리자를 반환합니다. 이 대리자를 사용하여 이벤트를 발생시키거나 Delegate 클래스의 메서드를 사용하여 처리기를 개별적으로 호출하세요.
참고
이 문서의 앞부분에 제공된 예제에 나타난 패턴을 따라, 대리자를 호출하기 전에 임시 변수에 복사하는 것이 좋습니다. 이렇게 하면 한 스레드가 마지막 처리기를 제거함으로써 다른 스레드가 대리자를 호출하기 직전에 해당 대리자가 null이 되는 경합 상태가 방지됩니다. 대리자는 변경할 수 없으므로 복사본은 여전히 유효합니다.
접근자에 적절하게 자체 코드를 삽입하세요. 스레드 안정성이 중요한 경우 코드에 대해 자체 잠금 기능을 제공해야 합니다.
C# 사용자: Windows 런타임 이벤트 패턴으로 사용자 지정 이벤트 접근자를 작성하는 경우 컴파일러는 일반적인 구문 바로 가기를 제공하지 않습니다. 코드에서 이벤트 이름을 사용하면 컴파일러 오류가 발생합니다.
Visual Basic 사용자: .NET Framework에서 이벤트는 등록된 모든 이벤트 처리기를 나타내는 멀티캐스트 대리자일 뿐입니다. 이벤트를 발생시키는 것은 대리자를 호출하는 것과 같습니다. Visual Basic 구문은 일반적으로 대리자와의 상호 작용을 숨기며 스레드 안정성에 대한 참고 사항에서 설명했듯이 컴파일러는 대리자를 호출하기 전에 복사합니다. Windows 런타임 구성 요소에서 사용자 지정 이벤트를 만드는 경우 대리자를 직접 처리해야 합니다. 예를 들어 처리기를 별도로 호출하려고 하는 경우 MulticastDelegate.GetInvocationList 메서드를 사용하여 각 이벤트 처리기에 대한 별도의 대리자가 들어 있는 배열을 가져올 수도 있습니다.
참고 항목
작업
방법: 사용자 지정 이벤트 접근자 구현(C# 프로그래밍 가이드)
참조
기타 리소스
.NET Framework Support for Windows Store Apps and Windows Runtime