Generics and delegates

B

Bruce Wood

I'm trying to "genericize" the following class. At the moment I have
derived classes for each different type of event handler / event
arguments, and I wanted to have a single, generic, catch-all class for
wrapping an event subscription in a "weak" event handler.

Unfortunately, I've been bitten by the restriction that one can't do
this:

public class Blah<T> : WeakReference where T : Delegate

because the compiler complains that "System.Delegate" isn't a valid
constraint. Can anyone give me a pointer or two on how to restructure
this to create a generic weak reference delegate? (The code has been
stripped down to remove some comments and error checking, and no, it
doesn't compile as written.)

/// <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<A, H> : WeakReference
{
private EventInfo _provider;
private MethodInfo _subscriberMethod;

protected WeakEventReference(object subscriber, MethodInfo
subscriberMethod, EventInfo provider) : base(subscriber)
{
this._subscriberMethod = subscriberMethod;
ParameterInfo[] subscriberMethodParameters =
subscriberMethod.GetParameters();
this._provider = provider;
}

protected WeakEventReference(object subscriber, Type
subscriberType, string subscriberMethod, Type providerType, string
providerEvent) : base(subscriber)
{
this._subscriberMethod =
subscriberType.GetMethod(subscriberMethod, BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
ParameterInfo[] subscriberMethodParameters =
this._subscriberMethod.GetParameters();
if (providerType == null)
{
this._provider = null;
}
else
{
this._provider = providerType.GetEvent(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; }
}

/// <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="args">Arguments giving more information about
the event.</param>
public void Handler(object sender, A args)
{
object sub = this.Target;
if (sub != null)
{
this.SubscriberMethod.Invoke(sub, new object[]
{ sender, args });
}
else if (this.Provider != null)
{
// Error here: H is not a System.Delegate
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 H Delegate
{
// Error here: H is not "newable"
get { return new H(this.Handler); }
}
}
 
N

Nicholas Paldino [.NET/C# MVP]

Bruce,

While it is not an optimal solution, you could perform a run-time check,
where in the static constructor for the type you check T to make sure that
it derives from Delegate. It's not optimal, but it's the only choice you
have with the current constraint system.

If the delegates that you are going to handle always follow the standard
pattern (first parameter is of type Object, second parameter of a type
deriving from EventArgs), then you could make the constraint on EventArgs,
and assume that the parameter list will adhere to the standard event
pattern.


--
- Nicholas Paldino [.NET/C# MVP]
- (e-mail address removed)

Bruce Wood said:
I'm trying to "genericize" the following class. At the moment I have
derived classes for each different type of event handler / event
arguments, and I wanted to have a single, generic, catch-all class for
wrapping an event subscription in a "weak" event handler.

Unfortunately, I've been bitten by the restriction that one can't do
this:

public class Blah<T> : WeakReference where T : Delegate

because the compiler complains that "System.Delegate" isn't a valid
constraint. Can anyone give me a pointer or two on how to restructure
this to create a generic weak reference delegate? (The code has been
stripped down to remove some comments and error checking, and no, it
doesn't compile as written.)

/// <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<A, H> : WeakReference
{
private EventInfo _provider;
private MethodInfo _subscriberMethod;

protected WeakEventReference(object subscriber, MethodInfo
subscriberMethod, EventInfo provider) : base(subscriber)
{
this._subscriberMethod = subscriberMethod;
ParameterInfo[] subscriberMethodParameters =
subscriberMethod.GetParameters();
this._provider = provider;
}

protected WeakEventReference(object subscriber, Type
subscriberType, string subscriberMethod, Type providerType, string
providerEvent) : base(subscriber)
{
this._subscriberMethod =
subscriberType.GetMethod(subscriberMethod, BindingFlags.Instance |
BindingFlags.NonPublic | BindingFlags.Public);
ParameterInfo[] subscriberMethodParameters =
this._subscriberMethod.GetParameters();
if (providerType == null)
{
this._provider = null;
}
else
{
this._provider = providerType.GetEvent(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; }
}

/// <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="args">Arguments giving more information about
the event.</param>
public void Handler(object sender, A args)
{
object sub = this.Target;
if (sub != null)
{
this.SubscriberMethod.Invoke(sub, new object[]
{ sender, args });
}
else if (this.Provider != null)
{
// Error here: H is not a System.Delegate
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 H Delegate
{
// Error here: H is not "newable"
get { return new H(this.Handler); }
}
}
 

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