Usando PostSharp es posible ajustar el ensamblado compilado en un paso posterior a la compilación. Esto le permite aplicar 'aspectos' al código, resolviendo problemas transversales.
Aunque las comprobaciones nulas o la inicialización de un delegado vacío pueden ser un problema menor, escribí un aspecto que lo resuelve agregando un delegado vacío a todos los eventos en un ensamblaje.
Su uso es muy fácil:
[assembly: InitializeEventHandlers(AttributeTargetTypes = "Main.*")]
namespace Main
{
...
}
I discussed the aspect in detail on my blog. En caso de que tenga PostSharp, aquí está el aspecto:
/// <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);
}
}
... y el método de ayuda que utiliza:
/// <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);
}
Ver: http://www.dailycoding.com/Posts/avoiding_event__null_check.aspx para una explicación expandida. –