Adding same method twice to delegate

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);

?
 
T

Truong Hong Thi

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
 
I

Ignacio Machin \( .NET/ C# MVP \)

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

Bruce Wood

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

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