通用弱事件管理器助手

时间:2019-03-10 12:25:00

标签: c# .net weak-events

如果要以通用可靠的方式使用System.Windows.WeakEventManager(组件WindowsBase),如何:

  1. 避免不必要地注册静态处理程序?
  2. 确保该事件在源上存在。

由于考虑到使用,可能会有一些例外。如何处理?

WeakEventManager<TEventSource, TEventArgs>
  .AddHandler(TEventSource source, string eventName, EventHandler<TEventArgs> handler);
WeakEventManager<TEventSource, TEventArgs>
  .RemoveHandler(TEventSource source, string eventName, EventHandler<TEventArgs> handler);

1 个答案:

答案 0 :(得分:0)

如果性能不是问题,则通用帮助程序可以像下面的代码一样完成。

由于(静态程序集的)静态处理程序将随应用程序域一起卸载,因此无需为此使用弱引用。

通用事件注册/注销实现中可能会出现很多例外情况。

特别是,根据您的需求实施弱管理可能会更好。

public static class WeakEventManagerHelper
{

  public static void AddEventHandler<T, U>(string eventName, T eventSource, EventHandler<U> handler) where U : EventArgs
  {
    ThrowOnNullArg(eventName, eventSource, handler);

    EventInfo eventInfo = GetEventInfo(eventName, eventSource);
    ThrowIfEventNotExists(eventInfo, eventName);

    if (!eventInfo.AddMethod.IsPublic)
    {
      ThrowIfEventMethodIsNotPublic("add");
    }

    ThrowIfHandlerIsNotProper(eventInfo, handler);

    if (handler.Method.IsStatic && !IsCollectibleAssembly(handler))
    {
      AddStaticEventHandler(eventInfo, eventSource, handler);
    }
    else
    {
      AddInstanceEventHandler(eventName, eventSource, handler);
    }
  }       

  public static void RemoveEventHandler<T, U>(string eventName, T eventSource, EventHandler<U> handler) where U : EventArgs
  {
    ThrowOnNullArg(eventName, eventSource, handler);

    EventInfo eventInfo = GetEventInfo(eventName, eventSource);
    ThrowIfEventNotExists(eventInfo, eventName);

    if (!eventInfo.RemoveMethod.IsPublic)
    {
      ThrowIfEventMethodIsNotPublic("remove");
    }

    ThrowIfHandlerIsNotProper(eventInfo, handler);

    if (handler.Method.IsStatic && !IsCollectibleAssembly(handler))
    {
      RemoveStaticEventHandler(eventInfo, eventSource, handler);
    }
    else
    {
      RemoveInstanceEventHandler(eventName, eventSource, handler);
    }
  }

  private static void AddStaticEventHandler(EventInfo eventInfo, object eventSource, Delegate handler)
  {
    try
    {
      eventInfo.AddEventHandler(eventSource, handler);
    }
    catch
    {
      // Handling …
      throw;
    }      
  }

  private static void RemoveStaticEventHandler(EventInfo eventInfo, object eventSource, Delegate handler)
  {
    try
    {
      eventInfo.RemoveEventHandler(eventSource, handler);
    }
    catch
    {
      // Handling …
      throw;
    }      
  }

  private static void AddInstanceEventHandler<T, U>(string eventName, T eventSource, EventHandler<U> handler) where U : EventArgs
  {
    try
    {
      WeakEventManager<T, U>.AddHandler(
      eventSource,
      eventName,
      handler);
    }
    catch
    {
      // Handling …
      throw;
    }
  }

  private static void RemoveInstanceEventHandler<T, U>(string eventName, T eventSource, EventHandler<U> handler) where U : EventArgs
  {
    try
    {
      WeakEventManager<T, U>.RemoveHandler(
      eventSource,
      eventName,
      handler);
    }
    catch
    {
      // Handling …
      throw;
    }
  }

  private static EventInfo GetEventInfo<T>(string eventName, T eventSource)
  {
    return eventSource.GetType().GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
  }

  private static void ThrowOnNullArg<T, U>(string eventName, T eventSource, EventHandler<U> handler) where U : EventArgs
  {
    if (eventName == null)
    {
      throw new ArgumentNullException(nameof(eventName));
    }
    else if (eventSource == null)
    {
      throw new ArgumentNullException(nameof(eventSource));
    }
    else if (handler == null)
    {
      throw new ArgumentNullException(nameof(handler));
    }
  }

  private static void ThrowIfEventNotExists(EventInfo eventInfo, string eventName)
  {
    if (eventInfo == null)
    {
      throw new ArgumentException($"Event source does not contain event named {eventName}!");
    }
  }

  private static void ThrowIfEventMethodIsNotPublic(string method)
  {
    throw new ArgumentException($"Event {method} method is not public!");
  }

  private static void ThrowIfHandlerIsNotProper(EventInfo eventInfo, Delegate handler)
  {
    if (eventInfo.EventHandlerType != handler.GetType())
    {
      throw new ArgumentException($"Improper handler type {handler}!");
    }
  }    

  private static bool IsCollectibleAssembly<U>(EventHandler<U> handler)
  {
    if (!handler.Method.DeclaringType.Assembly.IsDynamic)
    {
      return false;
    }

    try
    {
      Assembly handlerMethodAssembly = handler.Method.DeclaringType.Assembly;

      Type assemblyBuilderDataType = Assembly.GetAssembly(typeof(AssemblyBuilder))
          .GetType("System.Reflection.Emit.AssemblyBuilderData");

      object assemblyBuilderData = handlerMethodAssembly.GetType()
        .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
        .Single(fi => fi.FieldType == assemblyBuilderDataType)
        .GetValue(handlerMethodAssembly);

      object assemblyBuilderAccess = assemblyBuilderDataType
        .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
        .Single(fi => fi.FieldType == typeof(AssemblyBuilderAccess))
        .GetValue(assemblyBuilderData);

      return (AssemblyBuilderAccess)assemblyBuilderAccess == AssemblyBuilderAccess.RunAndCollect;
    }
    catch (Exception)
    {
      return false;
    }
  }
}