이벤트 처리기가 null인지 확인하는 경우 스레드별로이 작업이 수행됩니까?
누군가가 이벤트를 듣고 있는지 확인하는 것은 다음과 같이 수행됩니다.
EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);
위의 패턴을 따라 코드를 추가하면 null을 확인한 다음 null 체크가 필요한 이유는 무엇입니까?이 사이트에서 가져온 코드). 내가 뭘 놓치고 있니?
또한 이벤트 및 GC의 규칙은 무엇입니까?
당신이 의미하는 바가 무엇인지는 분명치 않습니다. 그러나 델리게이트가 null 일 가능성이 있다면, 각 스레드에서 개별적으로 체크해야합니다. 일반적으로 다음을 수행합니다.
public void OnSeven()
{
DivBySevenHandler handler = EventSeven;
if (handler != null)
{
handler(...);
}
}
이렇게하면EventSeven
과정 중에 변화OnSeven()
너는NullReferenceException
.
하지만 구독 처리기가 확실히있는 경우 null 체크가 필요하지 않습니다. 이것은 C #2에서 "no-op"핸들러를 사용하여 쉽게 수행 할 수 있습니다.
public event DivBySevenHandler EventSeven = delegate {};
반면에, 당신은힘다양한 스레드로부터 서브 스크립 션을 얻을 수있는 경우, "최신"핸들러 세트가 있는지 확인하기 위해 일종의 잠금을 원합니다. 나는내 스레딩 튜토리얼의 예제도움이 될 수 있습니다 - 일반적으로 나는 그것을 요구하지 않으려 고 노력하는 것이 좋습니다.
가비지 콜렉션 측면에서, 이벤트발행자이벤트에 대한 참조로 끝난다.구독자(즉, 핸들러의 목표). 게시자가 구독자보다 오래 거주하려는 경우에만 문제가됩니다.
EventSeven?.Invoke(....)
. 스레드 안전합니까? - Hossein Narimani Rad
문제는 아무도 이벤트에 가입하지 않으면 null입니다. 그리고 null에 대해 호출 할 수 없습니다. 3 가지 접근법이 떠오른다.
public event EventHandler MyEvent = delegate {};
null을 검사 할 때 스레드로부터 안전하려면 반드시이론에 의하면위임 참조를 먼저 캡처하십시오 (체크와 호출 사이에서 변경되는 경우).
protected virtual void OnMyEvent() {
EventHandler handler = MyEvent;
if(handler != null) handler(this, EventArgs.Empty);
}
확장 메서드는 null 인스턴스에서 호출 할 수있는 특이한 속성을가집니다.
public static void SafeInvoke(this EventHandler handler, object sender)
{
if (handler != null) handler(sender, EventArgs.Empty);
}
public static void SafeInvoke<T>(this EventHandler<T> handler,
object sender, T args) where T : EventArgs
{
if (handler != null) handler(sender, args);
}
다음을 호출 할 수 있습니다.
MyEvent.SafeInvoke(this);
검사를 통해 널 (null) - 안전하고 스레드 안전 (참조를 한 번만 읽음)합니다.
C #6.0에 대한 간단한 정보를 추가하고 싶습니다. - 구문 :
이제 이것을 대체 할 수 있습니다 :
var handler = EventSeven;
if (handler != null)
handler.Invoke(this, EventArgs.Empty);
이걸로 :
handler?.Invoke(this, EventArgs.Empty);
protected virtual void OnMyEvent()
{
EventHandler handler = MyEvent;
handler?.Invoke(this, EventArgs.Empty);
}
~에 이르기까지짧막 한 농담:
protected virtual void OnMyEvent() => MyEvent?.Invoke(this, EventArgs.Empty);
MyEvent?.Invoke
안전하다.handler?.Invoke
. null 전파 전파가 원자 적입니까? - Teejay
이벤트 처리기를 시작하기 전에 이벤트 처리기를 확인하는 것이 좋습니다. 나는 처음에 그것이 항상 설정된다는 것을 나 자신에게 "보증"한다해도 이것을한다. 나중에 이걸 변경하면 내 모든 이벤트 발사를 확인할 필요가 없습니다. 그래서 각 이벤트마다 항상 다음과 같은 OnXXX 메서드가 있습니다.
private void OnEventSeven()
{
var handler = EventSeven;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
외부 호출자가 마음대로 이벤트 처리기를 추가하거나 제거 할 수 있기 때문에 이벤트 처리기가 클래스에 공용 인 경우 특히 중요합니다.
당신이 이것을 의미한다면 :
public static void OnEventSeven(DivBySevenEventArgs e)
{
if(EventSeven!=null)
EventSeven(new object(),e);
}
코드 조각이면 대답은 다음과 같습니다.
아무도 "EventSeven"이벤트 핸들러에 가입하지 않으면 "EventSeven (new object (), e);에 널 참조 예외가 발생합니다."
그리고 규칙 :
구독자는 더 이상 이벤트를 수신하지 않으려는 경우 처리기 (+ =)를 추가하고 제거 (- =) 할 책임이 있습니다. 객체가 더 이상 참조 될 수 없으면 객체를 정리할 수 있지만 가비지 수집은 기본 규칙에 따라 진행됩니다.
this
), 임의로 새로 생성 된 객체가 아닌? - Rowland Shaw
사용포스트 샤프컴파일 후 단계에서 컴파일 된 어셈블리를 조정할 수 있습니다. 이를 통해 코드에 'aspect'를 적용하여 교차 절단 문제를 해결할 수 있습니다.
null 검사 또는 빈 대리자 초기화가 매우 사소한 문제 일지라도 어셈블리의 모든 이벤트에 빈 대리자를 추가하여이를 해결하는 측면을 썼습니다.
사용법은 매우 쉽습니다.
[assembly: InitializeEventHandlers( AttributeTargetTypes = "Main.*" )]
namespace Main
{
...
}
나는내 블로그에 세부 사항을 논의했다.. PostSharp를 사용하는 경우 다음과 같은 측면이 있습니다.
/// <summary>
/// Aspect which when applied on an assembly or class, initializes all the event handlers (<see cref="MulticastDelegate" />) members
/// in the class(es) with empty delegates to prevent <see cref="NullReferenceException" />'s.
/// </summary>
/// <author>Steven Jeuris</author>
[AttributeUsage( AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Event )]
[MulticastAttributeUsage( MulticastTargets.Event, AllowMultiple = false )]
[AspectTypeDependency( AspectDependencyAction.Commute, typeof( InitializeEventHandlersAttribute ) )]
[Serializable]
public class InitializeEventHandlersAttribute : EventLevelAspect
{
[NonSerialized]
Action<object> _addEmptyEventHandler;
[OnMethodEntryAdvice, MethodPointcut( "SelectConstructors" )]
public void OnConstructorEntry( MethodExecutionArgs args )
{
_addEmptyEventHandler( args.Instance );
}
// ReSharper disable UnusedMember.Local
IEnumerable<ConstructorInfo> SelectConstructors( EventInfo target )
{
return target.DeclaringType.GetConstructors( BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic );
}
// ReSharper restore UnusedMember.Local
public override void RuntimeInitialize( EventInfo eventInfo )
{
base.RuntimeInitialize( eventInfo );
// Construct a suitable empty event handler.
MethodInfo delegateInfo = DelegateHelper.MethodInfoFromDelegateType( eventInfo.EventHandlerType );
ParameterExpression[] parameters = delegateInfo.GetParameters().Select( p => Expression.Parameter( p.ParameterType ) ).ToArray();
Delegate emptyDelegate
= Expression.Lambda( eventInfo.EventHandlerType, Expression.Empty(), "EmptyDelegate", true, parameters ).Compile();
// Create a delegate which adds the empty handler to an instance.
_addEmptyEventHandler = instance => eventInfo.AddEventHandler( instance, emptyDelegate );
}
}
... 그리고 그것을 사용하는 도우미 메서드 :
/// <summary>
/// The name of the Invoke method of a Delegate.
/// </summary>
const string InvokeMethod = "Invoke";
/// <summary>
/// Get method info for a specified delegate type.
/// </summary>
/// <param name = "delegateType">The delegate type to get info for.</param>
/// <returns>The method info for the given delegate type.</returns>
public static MethodInfo MethodInfoFromDelegateType( Type delegateType )
{
Contract.Requires( delegateType.IsSubclassOf( typeof( MulticastDelegate ) ), "Given type should be a delegate." );
return delegateType.GetMethod( InvokeMethod );
}