When objects are set to null...removing events

M

MikeT

This may sound very elementary, but can you trap when your object is
set to null within the object?

I have created a class that registers an event from an object passed
in the constructor. When my object is destroyed, I want my object to
un-register this event. If I don't then the object would never be
destroyed until the object I passed in the constructor is destroyed.

I have implemented a Dispose(), Dispose(bool), and ~Finalize method,
where the finalize calls Dispose. But this is only called when the
object is Garbage Collected.

If the developer using my object uses one of the following statements,
I want my object to unregister the event:

myObject = null;

or

myObject = new myObject()

Is this possible? If not, can I force the developer to call the
Dispose method?

I imagine other have run into this scenario, what have you done?

Thanks to anyone that can help.
 
A

Alex Meleta

The expression "myObject = null" refs myObject to null reference (not
refer to any object) and GC will collect it by the next iteration. If
developer determinates an object as IDisposable he should use the
"using" statement
(http://msdn2.microsoft.com/en-us/library/yh598w02.aspx)

By the way u should provide the "GC.SuppressFinalize(this)" in the
..Dispose method to suppress unnecessary Finalize call.

Regards, Alex Meleta
Blog:: http://devkids.blogspot.com


-----Original Message-----
From: MikeT [mailto:[email protected]]
Posted At: Wednesday, April 25, 2007 3:36 PM
Posted To: microsoft.public.dotnet.languages.csharp
Conversation: When objects are set to null...removing events
Subject: When objects are set to null...removing events

This may sound very elementary, but can you trap when your object is
set to null within the object?

I have created a class that registers an event from an object passed
in the constructor. When my object is destroyed, I want my object to
un-register this event. If I don't then the object would never be
destroyed until the object I passed in the constructor is destroyed.

I have implemented a Dispose(), Dispose(bool), and ~Finalize method,
where the finalize calls Dispose. But this is only called when the
object is Garbage Collected.

If the developer using my object uses one of the following statements,
I want my object to unregister the event:

myObject = null;

or

myObject = new myObject()

Is this possible? If not, can I force the developer to call the
Dispose method?

I imagine other have run into this scenario, what have you done?

Thanks to anyone that can help.
 
B

Barry Kelly

MikeT said:
This may sound very elementary, but can you trap when your object is
set to null within the object?

You cannot.
I have created a class that registers an event from an object passed
in the constructor. When my object is destroyed, I want my object to
un-register this event.

The idiomatic way to implement this semantic in .NET is to implement
IDisposable, but it also requires that the users of your class play
along, by calling Dispose() as necessary, or alternatively using
instances of your class only in 'using' blocks.
I have implemented a Dispose(), Dispose(bool), and ~Finalize method,
where the finalize calls Dispose. But this is only called when the
object is Garbage Collected.

Finalizers are for very limited scenarios, such as wrapping unmanaged
resources. Unless you're interacting with the OS or a native library at
a very low level, you don't ever need to implement a finalizer.

You should implement IDisposable on your class; this involves having a
Dispose() method. The Dispose() method should call your protected
virtual Dispose(bool) method passing along 'true', and you should
unregister your event handler in the Dispose(bool) method roughly like
this:

---8<---
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
someOtherObject.TheEvent -= MyHandler;
}
}
--->8---

If there were such a thing as weak delegates in .NET, what your after
would be somewhat more possible, but it would still be non-deterministic
due to GC.
If the developer using my object uses one of the following statements,
I want my object to unregister the event:

myObject = null;

or

myObject = new myObject()

Is this possible?
No.

If not, can I force the developer to call the
Dispose method?
No.

I imagine other have run into this scenario, what have you done?

Always dispose objects that implement IDisposable, and implement
IDisposable on objects which need disposing.

-- Barry
 
B

Barry Kelly

Alex said:
The expression "myObject = null" refs myObject to null reference (not
refer to any object) and GC will collect it by the next iteration.

It won't, because the OP has subscribed for an event. The delegate
instance in the event handler will keep the object alive, preventing
collection.

-- Barry
 
B

Bruce Wood

If there were such a thing as weak delegates in .NET, what your after
would be somewhat more possible, but it would still be non-deterministic
due to GC.

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.

So yes... it can be done. You just have to write it yourself. :)

I have some .NET 1.1 sample code at work... I can post it. You're
better off changing it to use generics, though. Then you get a general-
purpose wrapper that you can use for weak subscription to any event.
 
M

MikeT

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.
 
B

Bruce Wood

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
 
M

MikeT

Thanks for the sample code. I couldn't get it to work and could not
figure out why.

This is what I got from what you posted:
- You created an abstract class of "WeakEventReference". This class
is used to create weak wrappers for specific events.
- The "SystemEventWeakReference" is a weak wrapper for a system event.

I tried creating a class that inherited "WeakEventReference". I added
a Delegate property and Handler method, as you did with
"SystemEventWeakReference".

The problem I had is with the constructor. I tried passing the
MethodInfo property and "GetMethod(string, BindingFlags)" does not
find the method in my class. I cannot see why !?!?!

Maybe an example of how you implemented this would be helpful.
 
B

Bruce Wood

Thanks for the sample code. I couldn't get it to work and could not
figure out why.

This is what I got from what you posted:
- You created an abstract class of "WeakEventReference". This class
is used to create weak wrappers for specific events.
- The "SystemEventWeakReference" is a weak wrapper for a system event.

I tried creating a class that inherited "WeakEventReference". I added
a Delegate property and Handler method, as you did with
"SystemEventWeakReference".

The problem I had is with the constructor. I tried passing the
MethodInfo property and "GetMethod(string, BindingFlags)" does not
find the method in my class. I cannot see why !?!?!

Maybe an example of how you implemented this would be helpful.

I'm at MIX'07 in Las Vegas until Wednesday. I've tagged this thread
and will get back and post a sample when I can.
 
B

Bruce Wood

Thanks for the sample code. I couldn't get it to work and could not
figure out why.

This is what I got from what you posted:
- You created an abstract class of "WeakEventReference". This class
is used to create weak wrappers for specific events.
- The "SystemEventWeakReference" is a weak wrapper for a system event.

I tried creating a class that inherited "WeakEventReference". I added
a Delegate property and Handler method, as you did with
"SystemEventWeakReference".

The problem I had is with the constructor. I tried passing the
MethodInfo property and "GetMethod(string, BindingFlags)" does not
find the method in my class. I cannot see why !?!?!

Maybe an example of how you implemented this would be helpful.

OK... here's a stitch of code where I subscribe to a static event via
a weak reference delegate:

Agama.ImageManagement.GlobalImageCache.DefaultMeasurementSystemChanged
+=
new SystemEventWeakReference(this, typeof(frmSCGProfile),
"GlobalImageCache_DefaultMeasurementSystemChanged",
typeof(Agama.ImageManagement.GlobalImageCache),
"DefaultMeasurementSystemChanged").Delegate;

Be aware that this is subscribing to a _static_ event. Subscribing to
an instance event would be a bit different.

The object that's subscribing is an instance of frmSCGProfile, one of
my forms. The event handling method within frmSCGProfile is called
GlobalImageCache_DefaultMeasurementSystemChanged. The event is
DefaultMeasurementSystemChanged, exposed by the GlobalImageCache
object.

Jon Skeet once pointed out to me that all of this could be improved,
or at least made typesafe, by using generics. That way, rather than
having a bunch of types that inherit from WeakReferenceEventHandler,
you could just have one generic type.
 
M

MikeT

OK... here's a stitch of code where I subscribe to a static event via
a weak reference delegate:

Agama.ImageManagement.GlobalImageCache.DefaultMeasurementSystemChanged
+=
new SystemEventWeakReference(this, typeof(frmSCGProfile),
"GlobalImageCache_DefaultMeasurementSystemChanged",
typeof(Agama.ImageManagement.GlobalImageCache),
"DefaultMeasurementSystemChanged").Delegate;

Be aware that this is subscribing to a _static_ event. Subscribing to
an instance event would be a bit different.

How would subscribing to an instance event be different? In my case,
I am subscribing to an instance event.

The provider is handled that a static class, but is really an
instance. It's a class created at application start and destroyed at
application exit. I could not use a static class because it will be
on a web server and the class methods/properties are user specific.
Jon Skeet once pointed out to me that all of this could be improved,
or at least made typesafe, by using generics. That way, rather than
having a bunch of types that inherit from WeakReferenceEventHandler,
you could just have one generic type.- Hide quoted text -

I had to inherit a class from WeakReferenceEventHandler because my
event delegate doesn't use EventArgs. I tried re-creating your class
using Generics but had a problem. From what I could see, the generic
type would have to be a Delegate, but you can't use a delegate as a
type.

For example, the constructor would look like this:

public class WeakEventReference<T> : WeakReference where T :
System.Delegate

and the Delegate method would be:

public T Delegate
{
get { return new T(this.Handler); }
}

But you can't make T a type of System.Delegate.

Maybe I'm using the wrong type for this or going in the wrong
direction?
 
B

Bruce Wood

Thanks for the sample code. I couldn't get it to work and could not
figure out why.

This is what I got from what you posted:
- You created an abstract class of "WeakEventReference". This class
is used to create weak wrappers for specific events.
- The "SystemEventWeakReference" is a weak wrapper for a system event.

I tried creating a class that inherited "WeakEventReference". I added
a Delegate property and Handler method, as you did with
"SystemEventWeakReference".

The problem I had is with the constructor. I tried passing the
MethodInfo property and "GetMethod(string, BindingFlags)" does not
find the method in my class. I cannot see why !?!?!

Maybe an example of how you implemented this would be helpful.

Could you post the code in which you call the constructor?

When I said that "instance methods would be different" I was referring
to a few tweaks that you might have to make in order to get them to
work. There are two assumptions baked into the code I posted:

1. That the events are static events.
2. That the subscribing methods are instance methods. This second
assumption is just fine, as it makes no sense to use weak references
to point to static subscribers, since static objects are never garbage
collected.

So, it's the first assumption that is causing you problems.

Now, the weak reference event handler knows several things about the
event to which it is subscribing, but notice that you never have to
pass it an actual _instance_ of the "provider". You pass it a provider
type, and a provider event name (or the EventInfo, which is equivalent
to those two), but you never pass it an instance. This means that you
say, "I want to subscribe to event SomethingHappened of objects of
type MyClass, but you never actually provide an object. Clearly this
can work only with static events.

If you want to adapt this code for instance events, then you have to
add another argument to the WeakEventReference constructors, and by
transitivity, to all of its child class's constructors. You need to
specify not only the providerType and the providerEvent, but also a
provider object. This is all in aid of having the wrapper unsubscribe
itself when it detects that its subscriber has been garbage collected:
it really doesn't have anything to do with subscribing to the event in
the first place, which is why I asked you to supply the code where you
are subscribing.

You would subscribe to the adapted code (which follows) like this:

GlobalImageCache cacheObject = new GlobalImageCache();
cacheObject.CurrentMeasurementSystemChanged += new
SystemEventWeakReference(this, typeof(ImagePanel),
"Instance_CurrentMeasurementSystemChanged", cacheObject,
typeof(GlobalImageCache), "CurrentMeasurementSystemChanged").Delegate;

As you can see, not much changes: the constructor has one more
argument (see below) to allow it to un-subscribe from the event after
its target is garbage collected. That, and the event specification
(before the +=) changes, but that is part of the caller's code, not
part of the weak event reference classes.

Anyway, I've hand-adapted the code that I'm posting below. I don't
guarantee that it works, or even compiles; it's just to give you an
idea of how to proceed. I took out all of the comments to make the
post more compact:

public abstract class WeakEventReference : WeakReference
{
private object _provider;
private EventInfo _providerEvent;
private MethodInfo _subscriberMethod;

protected WeakEventReference(object subscriber, MethodInfo
subscriberMethod, object provider, EventInfo providerEvent, 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 (provider == null)
{
throw new ArgumentNullException("provider");
}
if (!providerType.IsAssignableFrom(provider.GetType())
{
throw new ArgumentException(String.Format("Object of
type {0} is not compatible with type {1} which provides events for
this wrapper.", provider.GetType(), providerType));
}
this._providerEvent = providerEvent;
if (!
this._providerEvent.EventHandlerType.Equals(eventHandlerType))
{
throw new ArgumentException(String.Format("EventInfo
is for a {0} event handler, not a {1}",
this._providerEvent.EventHandlerType, eventHandlerType),
"providerEvent");
}
}

protected WeakEventReference(object subscriber, Type
subscriberType, string subscriberMethod, object provider, 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;
this._providerEvent = null;
}
else
{
this._provider = provider;
if (provider == null)
{
throw new ArgumentNullException("provider");
}
if (!providerType.IsAssignableFrom(provider.GetType())
{
throw new ArgumentException(String.Format("Object
of type {0} is not compatible with type {1} which provides events for
this wrapper.", provider.GetType(), providerType));
}
if (providerEvent == null)
{
throw new ArgumentNullException("providerEvent");
}
this._providerEvent =
providerType.GetEvent(providerEvent);
if (this._providerEvent == null)
{
throw new
MissingMemberException(String.Format("Provider type '{0}' does not
publish a static event called '{1}'", providerType, providerEvent));
}
if (!
this._providerEvent.EventHandlerType.Equals(eventHandlerType))
{
throw new ArgumentException(String.Format("Event
provider event is for a {0}, not a {1}",
this._providerEvent.EventHandlerType, eventHandlerType),
"providerEvent");
}
}
}

protected object Provider
{
get { return this._provider; }
}

protected EventInfo ProviderEvent
{
get { return this._providerEvent; }
}

protected MethodInfo SubscriberMethod
{
get { return this._subscriberMethod; }
}
}

#endregion

#region SystemEventWeakReference

public class SystemEventWeakReference : WeakEventReference
{
public SystemEventWeakReference(object subscriber, MethodInfo
subscriberMethod, object provider, EventInfo providerEvent) :
base(subscriber, subscriberMethod, provider,
providerEvent, typeof(System.EventArgs), typeof(System.EventHandler))
{ }

public SystemEventWeakReference(object subscriber, Type
subscriberType, string subscriberMethod, object provider, Type
providerType, string providerEvent) :
base(subscriber, subscriberType, subscriberMethod,
providerType, provider, providerEvent, typeof(System.EventArgs),
typeof(System.EventHandler))
{ }

public SystemEventWeakReference(object subscriber, MethodInfo
subscriberMethod) : this(subscriber, subscriberMethod, null, null)
{ }

public SystemEventWeakReference(object subscriber, Type
subscriberType, string subscriberMethod) : this(subscriber,
subscriberType, subscriberMethod, null, null, null)
{ }

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.ProviderEvent != null)
{
this.ProviderEvent.RemoveEventHandler(this.Provider,
this.Delegate);
}
}

public System.EventHandler Delegate
{
get { return new System.EventHandler(this.Handler); }
}
}

#endregion
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top