简体   繁体   中英

Generic Weak Event Manager Helper

If one want to use System.Windows.WeakEventManager (assem. WindowsBase) in generic reliable way, how to:

  1. Avoid unnecessary registration of static handlers?
  2. Be sure that event exists on source.

Since the reflection comes in to the use, there could come some exceptions. How to handle them?

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

If performance is not the issue the generic helper could be done like code beneath.

Since static handler (of static assembly) will be unloaded with application domain, there is no need to use weak references for it.

There could come lot of exceptions in generic event registration/deregistration implementation.

Specifically, could be better to make your implementation of weak management per need.

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;
    }
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM