On Apr 26, 7:09 am, MikeT <therien.m...@gmail.comwrote:
This is another way to handle the problem. You can, in fact, build
weak event subscriber placeholders and use them to subscribe to
events. From the point of view of your object (the real subscriber)
the event subscription then "doesn't count" so far as garbage
collection is concerned: your object will be GC'd when the last
pointer to it goes out of scope, despite the event subscription.
The next time the event is raised, the tiny placeholder object, which
is still subscribed to the event, will detect that its target (to
which it holds a weak reference) is gone, and will unsubscribe itself.
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</emevents
/// 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</clike 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(su bscriber.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(eventHand lerType))
{
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</clike 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.GetTy pe()))
{
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(eventHand lerType))
{
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</clike 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</clike 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</clike this:
/// <code>
/// EventProviderType.StaticEvent += new
SystemEventWeakReference(this,
this.GetType().GetMethod("MyEventHandler")).Delega te;
/// </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</clike 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