Not with the code you showed. Here's an example:
er, I already said that the original code I showed was not what I intended
to use. I was originally unclear how events were related to delegates but
that is no longer an issue.
I agree that the code:
add
{
_theDelegate = value;
}
would overwrite the previous value...this is not the code I would use. As I
stated elsewhere, if I simply wanted to duplicate the existing semantics I
could either use
_theDelegate += value;
or
_theDelegate = Delegate.Combine(_theDelegate,value);
Neither of these is what I wound up using.
I suspect a List would actually give closer semantics to the "normal"
event semantics in terms of what happens if you add the same delegate
twice and remove it.
A List would work but would take more effort to manage.
For my purposes Dictionary provides the same external semantics and allows
me much greater control over the data that is associated with the delegate.
I can provide the same external semantics as a "normal" event.; that part is
easy.
Adding a delegate is easy; it captures the current synchronization context
and thread and saves that into a Dictionary entry, keyed by the delegate's
hashcode, wrapped in a class that contains all the context information,
which is then added to a list for that delegate instance. It's signature is:
Dictionary<int, List<EventContext>>
So long as each delegate instance yields a different hashcode it should work
without getting confused about delegate instances; can you think of a case
where this would not be true?
I originally used the delegate instance itself as the key but changed it to
use the hashcode so I could store a WeakReference to the delegate rather
then the delegate itself. This would allow subscribers that "forgot" to
unsubscribe to the event to be garbage collected (i.e. the delegate instance
itself would not keep the subscriber's instance alive). I currently have the
WeakReference disabled as it seemed to be garbage collecting instances that
I thought should not have been; that is something I intend to research
later.
As to removing it, that is more difficult to implement then simply adding a
delegate. Consider the case where the same Delegate instance is added
multiple times, but in several different contexts and threads, perhaps more
then once from each. This can occur in any number of combinations, with one
or more Delegate instances. When a delegate instance is removed there is no
guarantee that the call to Remove will occur in the same synchronization
context as the call to add the delegate, or from the same thread. The Remove
algorithm will first attempt to precisely match the current synchronization
context and threadID to the synchronization context/threadID used to add the
delegate, and if that cannot be located it attempts to find the best match
based on the Thread ID alone. If that fails then it defaults to removing the
first instance in the Dictionary's List that matches the Delegate instance
(which is similar to "normal" event behavior).
Good - in that case I hope you see why being able to call
GetInvocationList() on an event doesn't make sense (and how it might be
a pain to implement in your case anyway).
It was quite easy to implement, and it makes a lot of sense to do it because
the purpose was to provide synchronization semantics for each individual
subscriber. This requires handling each invocation differently so it was a
requirement that I either implement GetInvocationList or provide a semantic
equivalent. I implemented something quite similar, one that works with the
Dictionary.
Right. That all makes sense - but the straight assignment you used in
the sample code doesn't help you, and indeed it wouldn't help you for
the language to automatically generate a backing delegate.
We both agree that the straight assignment would not work without changing
the semantics; it's a minor point.
I think the language could have implemented events differently to make it
easier to work with. For example, they should have eliminated the need to
test the event itself for null before firing the event. I also don't like
that the legality of invoking methods (e.g. GetInvocationList) on events
changes depending on whether or not the Add/Remove accessors are
implemented - I did not expect that. They could have made these methods
virtual (or used some other mechanism) and allowed implementors to override
them.