C# 事件处理程序中空检入的使用
检查事件处理程序是否为null时,是否按每个线程执行 确保有人正在收听活动的方式如下:C# 事件处理程序中空检入的使用,c#,C#,检查事件处理程序是否为null时,是否按每个线程执行 确保有人正在收听活动的方式如下: EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen); private void OnEventSeven() { var handler = EventSeven; if (handler != null) { handler(this, EventArgs.Empty); } } 如果我在上面的检查
EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);
private void OnEventSeven()
{
var handler = EventSeven;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
如果我在上面的检查null的模式下添加代码,那么为什么我需要null检查()。我错过了什么
此外,事件和GC的规则是什么?在启动事件处理程序之前检查它始终是一种良好的做法。我这样做,即使我最初“保证”自己,它总是设置。如果我以后更改此选项,则不必检查所有事件触发。因此,对于每个事件,我都有一个附带的OnXXX方法,如下所示:
EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);
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);”上获得一个空引用异常
规则是:
订阅者不想再接收事件时,负责添加处理程序(+=)并删除它(-=)。垃圾收集遵循默认规则,如果对象不再被引用,则可以对其进行清理。问题是,如果没有人订阅事件,则该事件为空。并且不能对null调用。我想到了三种方法:
- 检查空值(见下文)
- 添加一个“不做任何事情”处理程序:
public event事件处理程序MyEvent=delegate{}代码>
- 使用扩展方法(见下文)
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);
}
而且它是空安全的(通过检查)和线程安全的(通过只读取一次引用)。我恐怕真的不清楚您的意思,但是如果委托可能是空的,您需要在每个线程上分别检查它。通常您会:
public void OnSeven()
{
DivBySevenHandler handler = EventSeven;
if (handler != null)
{
handler(...);
}
}
这确保了即使在OnSeven()
过程中EventSeven
发生更改,也不会出现NullReferenceException
但是如果您确实有一个订阅的处理程序,那么您不需要空检查,这是对的。这可以在C#2中使用“无操作”处理程序轻松完成:
另一方面,如果您可能从不同的线程获得订阅,您可能需要某种类型的锁定,以确保您拥有“最新”的处理程序集。我有一个能帮上忙的方法——尽管通常我建议尽量避免使用它
就垃圾收集而言,事件发布服务器最终会引用事件订阅服务器(即处理程序的目标)。只有当发布服务器的寿命比订阅服务器长时,这才是一个问题。使用它可以在编译后的步骤中调整已编译的程序集。这允许您将“方面”应用于代码,从而解决交叉关注点
尽管空检查或空委托初始化可能是一个非常小的问题,但我编写了一个方面,通过向程序集中的所有事件添加一个空委托来解决它
它的使用非常简单:
[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 );
}
}
//
///当应用于程序集或类时,初始化所有事件处理程序()成员的方面
///在具有空委托的类中,以防止。
///
///史蒂文·杰里斯
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Event)]
[MulticastAttributeUsage(MulticastTargets.Event,AllowMultiple=false)]
[AspectTypeDependencyAction.comment,typeof(InitializeEventHandlerAttribute))]
[可序列化]
公共类初始化EventHandlerAttribute:EventLevelAspect
{
[非串行化]
动作(加法运算);
[OnMethodEntryAdvice,MethodPointcut(“SelectConstructors”)]
构造入口上的公共无效(MethodExecutionArgs args)
{
_addEmptyEventHandler(args.Instance);
}
//ReSharper禁用UnusedMember.Local
IEnumerable选择器构造函数(EventInfo目标)
{
返回target.DeclaringType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
//ReSharper还原未使用的成员。本地
公共覆盖无效运行时初始化(EventInfo EventInfo)
{
base.RuntimeInitialize(eventInfo);
//构造合适的空事件处理程序。
MethodInfo delegateInfo=DelegateHelper.MethodInfoFromDelegateType(eventInfo.EventHandlerType);
ParameterExpression[]parameters=delegateInfo.GetParameters().Select(p=>Expression.Parameter(p.ParameterType)).ToArray();
委派成员
=Expression.Lambda(eventInfo.EventHandlerType,Expression.Empty(),“EmptyDelegate”,true,parameters).Compile();
//创建一个将空处理程序添加到实例的委托。
_addEmptyEventHandler=instance=>eventInfo.AddEventHandler(实例,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 );
}
//
///委托的Invoke方法的名称。
///
常量字符串InvokeMethod=“Invoke”;
///
///获取指定委托类型的方法信息。
///
///要获取其信息的委托类型。
///给定委托类型的方法信息。
公共静态MethodInfo MethodInfoFromDelegateType(类型delegateType)
{
Contract.Requires(delegateType.IsSubclassOf(typeof(MulticastDelegate)),“给定类型应为委托。”);
返回delegateType.GetMethod(InvokeMethod);
}
我想附加一些关于C#6.0语法的简短信息:
现在可以替换此选项:
var handler = EventSeven;
if (handler != null)
handler.Invoke(this, EventArgs.Empty);
为此:
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);