Daniel wrote:
I never talked of sender receiver so, i presume you are in agreement that
events do cause resource leaks if not unsubscribed from?
In some cases, yes. In most cases, no. An event subscription is a
reference like any other reference. If the event source is garbage
collected then the reference is effectively gone and the event
subscribe may also be garbage collection (if the event subscription is
the only reference left to it).
My issue is that my sender is a singleton class. And my receiver is an
instance created inside another singleton.
This is the only exception: if the event source is static then the
event reference will live forever and the subscriber will never be
garbage collected. In your case, the event source itself isn't static,
but it's a singleton, which means that there exists a static reference
to the instance which in turn contains an event (reference) to the
receiver, so the whole thing will live for the life of your
application.
Even after unsubscribing all events in my receiver instance inside the
singleton the instance still will not be garbage collected, even with a
forced dispose.
There is no such thing as a "forced dispose". You cannot force the
garbage collector to reclaim an object. You can ask it to run, but
whether it bothers reclaiming something is up to its internal
algorithms. Garbage collection is not deterministic.
One solution to this problem is to have your event subscribers
unsubscribe when they're no longer needed, but that comes with two
problems.
1. If you know when the subscriber is "no longer needed" you have to be
sure to call its Dispose() method or it will live forever. This flies
in the face of the architectural guidelines for Dispose(), which are
that it is nice to call it, but not necessary. Suddenly it becomes
necessary in the case of your event subscribers.
2. It's not always clear when the subscriber is "no longer needed." In
some cases the only way to figure out when an object is "no longer
needed" is to know when the last (important) reference to it
disappears, which means implementing reference counting, which means
creating an ugly hybrid of C# with some C++ features. Yuck.
A better solution is to use WeakReference when subscribing to the
event. This effectively says, "I, a subscriber, am subscribing to this
event, but I do not want my subscription taken into account when
deciding whether to garbage collect me." Here is some .NET 1.1 code for
doing this, although Jon Skeet has in the past pointed out that it can
be cleaned up significantly using generics in .NET 2.0, it's all I have
for now:
#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( 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(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