49

I have an event in a loop. I am trying to prevent the same method being added to an event more than once. I've implemented the add and remove accessors.

However, I get an error stating that:

ItemsProcessed can only appear on the left hand side of += or -=

When I try to call them, even within the same class.

ItemsProcessed(this, new EventArgs()); // Produces error

public event EventHandler ItemsProcessed
{
    add
    {
        ItemsProcessed -= value;
        ItemsProcessed += value;
    }
    remove
    {
        ItemsProcessed -= value;
    }
}
Ryan Gates
  • 4,501
  • 6
  • 50
  • 90
  • 3
    Also, it looks like you've created an infinite loop by accessing `ItemsProcessed` within the `ItemsProcessed` functions themselves. – Gabe Dec 21 '10 at 06:42
  • For reference, the `EventName(args)` invoke trick only applies to *field-like events*, where some operations implicitly resolve to the *field* (not the event) within the class. I have to say "some", because the exact list changed between C# 3 and C# 4. – Marc Gravell Dec 21 '10 at 06:45

4 Answers4

40

With an explicit event, you need to provide your own backing store - either a delegate field or something like EventHandlerList. The current code is recursive. Try:

private EventHandler itemsProcessed;
public event EventHandler ItemsProcessed
{
    add
    {
        itemsProcessed-= value;
        itemsProcessed+= value;
    }

    remove
    {
        itemsProcessed-= value;
    }
}

Then (and noting I'm being a little cautious about the "about to turn null" edge-case re threading):

var snapshot = itemsProcessed;
if(snapshot != null) snapshot(this, EventArgs.Empty);

With more recent C# versions, this can be simplified:

itemsProcessed?.Invoke(this, EventArgs.Empty);
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 5
    One disadvantage: Only accessible within a class – Abdul Munim Jan 05 '11 at 08:14
  • 3
    Why would you want to access a private backing event outside of a class? – Joshua Ramirez Mar 16 '12 at 15:57
  • 1
    Subclassing, duh. Make stuff protected. – Nyerguds Aug 02 '17 at 09:15
  • 6
    @Nyerguds subclassing rarely has a *good* reason to access backing fields; in the case of events, the more common approach is a `protected virtual void OnItemsProcessed() => itemsProcessed?.Invoke(this, EventArgs.Empty);` - job done, and no need to expose access to the field – Marc Gravell Aug 02 '17 at 10:01
  • Well I'm trying to make a custom `OnMouseWheel` that optionally executes the original linked `Control.MouseWheel` event... and I can't without also disabling the scrolling. Really frustrating. Mouse scroll is one of those weird semi-automatic things. – Nyerguds Aug 03 '17 at 16:51
16

I can't tell from your post if you are trying to raise the event from a derived class or not, but one thing I've found is that you can't define an event in a base class and then raise it (directly) in a derived class, for some reason that isn't real clear to me yet.

So I define protected functions in base classes to raise events (that are defined in those base classes), like this:

// The signature for a handler of the ProgressStarted event.
// title: The title/label for a progress dialog/bar.
// total: The max progress value.
public delegate void ProgressStartedType(string title, int total);

// Raised when progress on a potentially long running process is started.
public event ProgressStartedType ProgressStarted;

// Used from derived classes to raise ProgressStarted.
protected void RaiseProgressStarted(string title, int total) {
    if (ProgressStarted != null) ProgressStarted(title, total);
}

Then in the derived class, I call RaiseProgressStarted(title, total) instead of calling ProgressStarted(title, total).

It seems like kind of the long way around. Maybe someone else knows of a better way around this problem.

Shavais
  • 2,476
  • 1
  • 27
  • 25
  • 1
    I just realized this is a 5 year old post. I came across the problem and figured out what was going on for me, so I posted this answer. Maybe it'll be helpful to someone. – Shavais Jul 27 '15 at 19:20
  • 4
    Heh, I just had this problem again, couldn't quite remember what had caused it and how I'd fixed it, and found my own answer from 11 months ago. That's pretty funny. – Shavais Jun 30 '16 at 21:39
  • 2
    it's 2019, this fixed my problem, thanks for the 2015 post on the 2010 question! :-D – Ninjanoel Jul 11 '19 at 08:07
  • I figured that this must be the case, and it solves the problem, but what I'd really like to know is the reason. Perhaps this has become clear to someone in the past few years? – PeterT Jul 25 '19 at 11:15
  • https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/events/how-to-raise-base-class-events-in-derived-classes – Technophile Oct 22 '20 at 22:16
13

It seems that if you implement the EventHandler explicitly, you can't refer to the 'Property' when firing the event. You must refer to the backing store.

Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
John Robertson
  • 131
  • 1
  • 3
2

What error? I guess its stack overflow error, because you are calling add and remove on yourserlf (same event). Also you cannot raise event ACCESSOR.

Valid way to do this is to create backing private event, that will be added and removed to from public accessor, and you should raise this private event.

Dang, minute late.

Euphoric
  • 12,645
  • 1
  • 30
  • 44