This is an interesting concept. I would like to see some sample code,
so if you can post that would be great.
All the other comments that have been posted is what I expected. In
this particular project, my objects are used by other developers
within my company. However, most of them are new to .Net development
(ex-Powerbuilder), so I'm trying to prevent them from making a costly
mistake, such as not calling Dispose every time.
Here are my two basic classes for weak delegates. I can post some code
that uses them, later.
Sorry about the line wrapping and such. I tried to find one of those
free "paste buffer" sites on the Web, but couldn't track one down in
short order, so I'm posting here. If you know of one, I can post the
code there, which will make it more readable.
#region WeakEventReference
/// <summary>
/// Used to subscribe to <em>static</em> events
/// when the subscriber wants to subscribe via a weak reference
rather than a strong
/// reference.
/// </summary>
/// <remarks>Subscribing via a weak reference allows event
subscribers to be
/// garbage collected without having to unsubscribe to the event,
but it also
/// comes with a host of concurrency considerations, not the least
of which
/// is that the event handler method on the subscriber could be
called and be
/// executing while the object is being garbage collected!
/// <para>Subscribing via weak references is usually done when
subscribing to
/// static events, since the event supplier will never be garbage
collected,
/// and so anything to which it holds strong references (regular
event
/// subscriptions) will never be garbage collected.</para>
/// </remarks>
public abstract class WeakEventReference : WeakReference
{
private EventInfo _provider;
private MethodInfo _subscriberMethod;
/// <summary>
/// Creates a new weak reference wrapper for an event handler.
/// </summary>
/// <param name="subscriber">The object that wishes to subscribe
/// to the provider event.</param>
/// <param name="subscriberMethod">The method that should be
called
/// whenever the event is raised.</param>
/// <param name="provider">The event to which the subscriber is
subscribing. It is still
/// the caller's responsibility to subscribe to the event. This
event information
/// is used to unsubscribe from the event after the subscriber has
been garbage
/// collected.</param>
/// <param name="eventArgumentType">The type of event arguments
that
/// <paramref name="subscriberMethod"/> should accept as its
second
/// argument.</param>
/// <param name="eventHandlerType">The type of event handler that
/// <paramref name="provider"/> should be expecting as a
subscriber.
/// </param>
/// <exception cref="ArgumentException"><paramref
name="subscriberMethod"/>
/// does not accept two arguments: System.Object and
/// <paramref name="eventArgumentType"/>, or
/// <paramref name="provider"/> does not allow event handlers
/// of type <paramref name="eventHandlerType"/> to subscribe
/// to it.</exception>
/// <remarks>The caller should subscribe the <c>MyEventHandler</c>
method to the event
/// <c>StaticEvent</c> like this:
/// <code>
/// EventProviderType.StaticEvent += new
ItemAddingWeakReference(this, this.GetType().GetMethod("ItemAdded"),
///
typeof(EventProviderType).GetEvent("StaticEvent")).Delegate;
/// </code>
/// </remarks>
protected WeakEventReference(object subscriber, MethodInfo
subscriberMethod, EventInfo provider, Type eventArgumentType, Type
eventHandlerType) : base(subscriber)
{
if (subscriber == null)
{
throw new ArgumentNullException("subscriber");
}
if (subscriberMethod == null)
{
throw new ArgumentNullException("subscriberMethod");
}
if (!
subscriberMethod.DeclaringType.IsAssignableFrom(subscriber.GetType()))
{
throw new ArgumentException(String.Format("Cannot subscribe an
object of type {0} to a method with declaring type {1} because the
types are not compatible.", subscriber.GetType(),
subscriberMethod.DeclaringType), "subscriber");
}
this._subscriberMethod = subscriberMethod;
ParameterInfo[] subscriberMethodParameters =
subscriberMethod.GetParameters();
if (subscriberMethodParameters.Length != 2)
{
throw new ArgumentException(String.Format("The method
subscribing to the event takes {0} parameters, not the required two
parameters (object, {1}).", subscriberMethodParameters.Length,
eventArgumentType), "subscriberMethod");
}
else if (!
subscriberMethodParameters[0].ParameterType.Equals(typeof(object)))
{
throw new ArgumentException(String.Format("The method
subscribing to the event has its second parameter of type {0}, not the
required parameter type System.Object.",
subscriberMethodParameters[0]), "subscriberMethod");
}
else if (!
subscriberMethodParameters[1].ParameterType.Equals(eventArgumentType))
{
throw new ArgumentException(String.Format("The method
subscribing to the event has its second parameter of type {0}, not the
required parameter type {1}.", subscriberMethodParameters[1],
eventArgumentType), "subscriberMethod");
}
this._provider = provider;
if (!this._provider.EventHandlerType.Equals(eventHandlerType))
{
throw new ArgumentException(String.Format("EventInfo is for a
{0} event handler, not a {1}", this._provider.EventHandlerType,
eventHandlerType), "provider");
}
}
/// <summary>
/// Creates a new weak reference wrapper for an event handler.
/// </summary>
/// <param name="subscriber">The object that wishes to subscribe
/// to the provider event.</param>
/// <param name="subscriberType">The type of the subscriber. This
is passed explicitly
/// so to search for the <paramref name="subscriberMethod"/> at
the correct
/// level of the class hierarchy, since <paramref
name="subscriber"/> may be
/// a sub-class of <paramref name="subscriberType"/>.</param>
/// <param name="subscriberMethod">The method that should be
called
/// whenever the event is raised.</param>
/// <param name="providerType">The static type that defines the
event
/// to which the subscriber is subscribing.</param>
/// <param name="providerEvent">The name of the event to which the
subscriber is subscribing. It is still
/// the caller's responsibility to subscribe to the event. This
event information
/// is used to unsubscribe from the event after the subscriber has
been garbage
/// collected.</param>
/// <param name="eventArgumentType">The type of event arguments
that
/// <paramref name="subscriberMethod"/> should accept as its
second
/// argument.</param>
/// <param name="eventHandlerType">The type of event handler that
/// <paramref name="providerEvent"/> should be expecting as a
subscriber.
/// </param>
/// <exception cref="MissingMethodException">
/// <paramref name="subscriberType"/> does not declare a method
/// called <paramref name="subscriberMethod"/>.</exception>
/// <exception cref="MissingMemberException">
/// <paramref name="providerType"/> does not declare a static
event
/// called <paramref name="providerEvent"/>.</exception>
/// <exception cref="ArgumentException"><paramref
name="subscriberMethod"/>
/// does not accept two arguments: System.Object and
/// <paramref name="eventArgumentType"/>, or
/// <paramref name="providerEvent"/> does not allow event handlers
/// of type <paramref name="eventHandlerType"/> to subscribe
/// to it.</exception>
/// <remarks>The caller should subscribe the <c>MyEventHandler</c>
method to the event
/// <c>StaticEvent</c> like this:
/// <code>
/// EventProviderType.StaticEvent += new
ItemAddingWeakReference(this, this, "ItemAdded",
typeof(EventProviderType), "StaticEvent").Delegate;
/// </code>
/// </remarks>
protected WeakEventReference(object subscriber, Type
subscriberType, string subscriberMethod, Type providerType, string
providerEvent, Type eventArgumentType, Type eventHandlerType) :
base(subscriber)
{
if (subscriber == null)
{
throw new ArgumentNullException("subscriber");
}
if (subscriberType == null)
{
throw new ArgumentNullException("subscriberType");
}
if (subscriberMethod == null)
{
throw new ArgumentNullException("subscriberMethod");
}
if (!subscriberType.IsAssignableFrom(subscriber.GetType()))
{
throw new ArgumentException(String.Format("Cannot subscribe an
object of type {0} to a method with declaring type {1} because the
types are not compatible.", subscriber.GetType(), subscriberType),
"subscriber");
}
this._subscriberMethod =
subscriberType.GetMethod(subscriberMethod, BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
if (this._subscriberMethod == null)
{
throw new MissingMethodException(String.Format("Subscriber of
type '{0}' does not have an event handler method called '{1}'.",
subscriber.GetType(), subscriberMethod));
}
ParameterInfo[] subscriberMethodParameters =
this._subscriberMethod.GetParameters();
if (subscriberMethodParameters.Length != 2)
{
throw new ArgumentException(String.Format("The method
subscribing to the event takes {0} parameters, not the required two
parameters (object, {1}).", subscriberMethodParameters.Length,
eventArgumentType), "subscriberMethod");
}
else if (!
subscriberMethodParameters[0].ParameterType.Equals(typeof(object)))
{
throw new ArgumentException(String.Format("The method
subscribing to the event has its second parameter of type {0}, not the
required parameter type System.Object.",
subscriberMethodParameters[0]), "subscriberMethod");
}
else if (!
subscriberMethodParameters[1].ParameterType.Equals(eventArgumentType))
{
throw new ArgumentException(String.Format("The method
subscribing to the event has its second parameter of type {0}, not the
required parameter type {1}.", subscriberMethodParameters[1],
eventArgumentType), "subscriberMethod");
}
if (providerType == null)
{
this._provider = null;
}
else
{
if (providerEvent == null)
{
throw new ArgumentNullException("providerEvent");
}
this._provider = providerType.GetEvent(providerEvent);
if (this._provider == null)
{
throw new MissingMemberException(String.Format("Provider
type '{0}' does not publish a static event called '{1}'",
providerType, providerEvent));
}
if (!this._provider.EventHandlerType.Equals(eventHandlerType))
{
throw new ArgumentException(String.Format("Event provider
event is for a {0}, not a {1}", this._provider.EventHandlerType,
eventHandlerType), "providerEvent");
}
}
}
/// <summary>
/// Gets the event provider for the event to which the weak
/// reference delegate has subscribed.
/// </summary>
/// <value>Information about the event to which this weak
/// reference is subscribing.</value>
protected EventInfo Provider
{
get { return this._provider; }
}
/// <summary>
/// Gets the method information for the method to call on the
/// event subscriber.
/// </summary>
/// <value>Information about the method that this weak reference
/// is to call each time the event occurs.</value>
protected MethodInfo SubscriberMethod
{
get { return this._subscriberMethod; }
}
}
#endregion
#region SystemEventWeakReference
/// <summary>
/// Used to subscribe to static events that require <see
cref="System.EventHandler"/>,
/// when the subscriber wants to subscribe via a weak reference
rather than a strong
/// reference.
/// </summary>
/// <remarks>Subscribing via a weak reference allows event
subscribers to be
/// garbage collected without having to unsubscribe to the event,
but it also
/// comes with a host of concurrency considerations, not the least
of which
/// is that the event handler method on the subscriber could be
called and be
/// executing while the object is being garbage collected!
/// <para>Subscribing via weak references is usually done when
subscribing to
/// static events, since the event supplier will never be garbage
collected,
/// and so anything to which it holds strong references (regular
event
/// subscriptions) will never be garbage collected.</para>
/// </remarks>
public class SystemEventWeakReference : WeakEventReference
{
/// <summary>
/// Creates a new weak reference wrapper for an event handler.
/// </summary>
/// <param name="subscriber">The object that wishes to subscribe
/// to the provider event.</param>
/// <param name="subscriberMethod">The method that should be
called
/// whenever the event is raised.</param>
/// <param name="provider">The event to which the subscriber is
subscribing. It is still
/// the caller's responsibility to subscribe to the event. This
event information
/// is used to unsubscribe from the event after the subscriber has
been garbage
/// collected.</param>
/// <remarks>The caller should subscribe the <c>MyEventHandler</c>
method to the event
/// <c>StaticEvent</c> like this:
/// <code>
/// EventProviderType.StaticEvent += new
SystemEventWeakReference(this,
this.GetType().GetMethod("MyEventHandler"),
///
typeof(EventProviderType).GetEvent("StaticEvent")).Delegate;
/// </code>
/// </remarks>
public SystemEventWeakReference(object subscriber, MethodInfo
subscriberMethod, EventInfo provider) :
base(subscriber, subscriberMethod, provider,
typeof(System.EventArgs), typeof(System.EventHandler))
{ }
/// <summary>
/// Creates a new weak reference wrapper for an event handler.
/// </summary>
/// <param name="subscriber">The object that wishes to subscribe
/// to the provider event.</param>
/// <param name="subscriberType">The type of the subscriber. This
is passed explicitly
/// so to search for the <paramref name="subscriberMethod"/> at
the correct
/// level of the class hierarchy, since <paramref
name="subscriber"/> may be
/// a sub-class of <paramref name="subscriberType"/>.</param>
/// <param name="subscriberMethod">The method that should be
called
/// whenever the event is raised.</param>
/// <param name="providerType">The static type that defines the
event
/// to which the subscriber is subscribing.</param>
/// <param name="providerEvent">The name of the event to which the
subscriber is subscribing. It is still
/// the caller's responsibility to subscribe to the event. This
event information
/// is used to unsubscribe from the event after the subscriber has
been garbage
/// collected.</param>
/// <remarks>The caller should subscribe the <c>MyEventHandler</c>
method to the event
/// <c>StaticEvent</c> like this:
/// <code>
/// EventProviderType.StaticEvent += new
SystemEventWeakReference(this, this, "MyEventHandler",
typeof(EventProviderType), "StaticEvent").Delegate;
/// </code>
/// </remarks>
public SystemEventWeakReference(object subscriber, Type
subscriberType, string subscriberMethod, Type providerType, string
providerEvent) :
base(subscriber, subscriberType, subscriberMethod, providerType,
providerEvent, typeof(System.EventArgs), typeof(System.EventHandler))
{ }
/// <summary>
/// Creates a new weak reference wrapper for an event handler.
/// </summary>
/// <param name="subscriber">The object that wishes to subscribe
/// to the provider event.</param>
/// <param name="subscriberMethod">The method that should be
called
/// whenever the event is raised.</param>
/// <remarks>The caller should subscribe the <c>MyEventHandler</c>
method to the event
/// <c>StaticEvent</c> like this:
/// <code>
/// EventProviderType.StaticEvent += new
SystemEventWeakReference(this,
this.GetType().GetMethod("MyEventHandler")).Delegate;
/// </code>
/// Note that using this constructor does not allow the weak
reference wrapper to
/// unsubscribe from the event if its target is garbage collected,
and so the (admittedly tiny)
/// weak reference wrappers will build up in memory and event
delegate chains will get
/// longer and longer, cluttered with "dead" weak references.
Nonetheless, for small
/// applications where this may not matter, this constructor
offers a simpler calling sequence.
/// </remarks>
public SystemEventWeakReference(object subscriber, MethodInfo
subscriberMethod) : this(subscriber, subscriberMethod, null)
{ }
/// <summary>
/// Creates a new weak reference wrapper for an event handler.
/// </summary>
/// <param name="subscriber">The object that wishes to subscribe
/// to the provider event.</param>
/// <param name="subscriberType">The type of the subscriber. This
is passed explicitly
/// so to search for the <paramref name="subscriberMethod"/> at
the correct
/// level of the class hierarchy, since <paramref
name="subscriber"/> may be
/// a sub-class of <paramref name="subscriberType"/>.</param>
/// <param name="subscriberMethod">The method that should be
called
/// whenever the event is raised.</param>
/// <remarks>The caller should subscribe the <c>MyEventHandler</c>
method to the event
/// <c>StaticEvent</c> like this:
/// <code>
/// EventProviderType.StaticEvent += new
SystemEventWeakReference(this, "MyEventHandler")).Delegate;
/// </code>
/// Note that using this constructor does not allow the weak
reference wrapper to
/// unsubscribe from the event if its target is garbage collected,
and so the (admittedly tiny)
/// weak reference wrappers will build up in memory and event
delegate chains will get
/// longer and longer, cluttered with "dead" weak references.
Nonetheless, for small
/// applications where this may not matter, this constructor
offers a simpler calling sequence.
/// </remarks>
public SystemEventWeakReference(object subscriber, Type
subscriberType, string subscriberMethod) : this(subscriber,
subscriberType, subscriberMethod, null, null)
{ }
/// <summary>
/// The event handler that will really be subscribed to the event.
/// </summary>
/// <param name="sender">The object that raised the event.</param>
/// <param name="e">Arguments giving more information about the
event.</param>
public void Handler(object sender, System.EventArgs e)
{
object sub = this.Target;
if (sub != null)
{
this.SubscriberMethod.Invoke(sub, new object[] { sender, e });
}
else if (this.Provider != null)
{
this.Provider.RemoveEventHandler(null, this.Delegate);
}
}
/// <summary>
/// The delegate to add to the event dispatch chain.
/// </summary>
/// <value>The event handler delegate for this object's
/// <see cref="Handler"/> method.</value>
public System.EventHandler Delegate
{
get { return new System.EventHandler(this.Handler); }
}
}
#endregion