Adding same method twice to delegate

  • Thread starter Thread starter Bruce Wood
  • Start date Start date
B

Bruce Wood

Maybe I'm going nuts, but I was so sure that adding the same method
more than once to a delegate would result in only one entry on the
delegate's call list:

this.UpdateEnd += new EventHandler(UpdateStockCode);
this.UpdateEnd += new EventHandler(UpdateStockCode);

if (this.UpdateEnd != null)
{
this.UpdateEnd(this, System.EventArgs.Empty);
}

would result in only one call to UpdateStockCode(). However, it results
in two.

How do I ensure that there is only ever one copy of the method on the
delegate list, so that the method is only called once? Do I have to do
this:

this.UpdateEnd -= new EventHandler(UpdateStockCode);
this.UpdateEnd += new EventHandler(UpdateStockCode);

this.UpdateEnd -= new EventHandler(UpdateStockCode);
this.UpdateEnd += new EventHandler(UpdateStockCode);

?
 
Hi Bruce,

The documentation of Delegate.Combine and Delegate.Remove is very clear
about this. Also note that if you call Delegate.Remove, only the last
matched instance is removed.

You may add a method like this:
private void AddEvent(EventHandler handler)
{
// Only add if it does not already exist in the list
if (UpdateEnd == null || Array.IndexOf(UpdateEnd.GetInvocationList(),
handler) < 0)
{
UpdateEnd += handler;
}
}

And also take a look at the C# lang spec, you will see that you can
even "overload" the += op for your event, something like this:

private EventHandler updateEnd;
public event EventHandler UpdateEnd
{
add
{
if (updateEnd == null ||
Array.IndexOf(updateEnd.GetInvocationList(), value) < 0 )
{
updateEnd += value;
}
}

remove
{
updateEnd -= value;
}
}

Hope this helps,
Thi
 
Hi,

What if you really want to call the same method twice?

A better approach to your solution may be:

//you create one delegate once only
protected EventHandler UpdateEndMethod = new
EventHandler(UpdateStockCode);

//you can then do
this.UpdateEnd += UpdateEndMethod ;
this.UpdateEnd -= UpdateEndMethod ;


Maybe if you give more details about your particular situation a better
approach can be proposed.


cheers,
 
No, I never want to call the same method twice. In fact, the very
purpose of the event is to concentrate a bunch of other events into a
single invocation.

What I'm doing is using events to help me construct derived values
within a class. My class is a validator that accepts a database object
and then allows callers to change one field at a time. (It's the model
side of a maintenance screen.) When you load up a new business object
into the validator, that object might expose, say, properties A and B
(along with AChanged and BChanged events, etc.)

However, there may also be a derived property C that is calculated
based on A and B. Up until yesterday I was simply inserting code into
the "set" methods of A and B to recalculate C whenever either changed,
and raise the CChanged event accordingly. However, as the model became
more and more complicated, I started getting large numbers of events
(up to 7) for the same property, as the various properties upon which
it depended changed one by one and caused a recalculation of the
derived property. All of this several times over for several derived
properties. The process looked something like this (for the simple
example given):

this.A = something;
A's setter calls a method to recalculate C
Raise CChanged
Raise AChanged
this.B = something;
B's setter calls a method to recalculate C again
Raise CChanged
Raise BChanged

Essentially, the problem was that there was no way to tell the derived
properties to hold off recalculating themselves because a bunch of
things were about to change, and it would be better to wait until the
dust settled and recalculate then.

That's when I came up with the idea of using the xxxChanged events
within the model itself, and building a kind of BeginUpdate / EndUpdate
structure. So, now the process looks something like this:

this.AChanged += new EventHandler(RecalculateC);
this.BChanged += new EventHandler(RecalculateC);
....
BeginUpdate
this.A = something;
Raise AChanged
this.B = something;
Raise BChanged;
EndUpdate -> Raise UpdateEnded event
RecalculateC;
Raise CChanged;

and it all works because "RecalculateC" looks something like this:

private void RecalculateC(object sender, EventArgs e)
{
if (this.UpdateInProgress)
{
this.UpdateEnded -= new EventHandler(RecalculateC);
this.UpdateEnded += new EventHandler(RecalculateC);
}
else
{
... recalculate C and raise CChanged event ....
}
}

Of course, I need the "this.UpdateEnded -= new
EventHandler(RecalculateC);" line because otherwise "RecalculteC" will
be called twice, because "RecalculateC" was originally called twice
while the update was in progress in response to the AChanged and
BChanged events.

So, I need to play that little trick with the UpdateEnded event to get
the behaviour I want, which is "no matter how many times I'm called
while an update is in progress, I want to be invoked once when the
update ends."
 
Back
Top