Question

I am kind of new to programming in this manner - is there a way that I can work around or a recommended practice to using events and handlers?

eg:

class objectA
{    
    public List<Handler> handlers;
    ...
    public onActionHappened
    {
        foreach(Handler h in handlers)
        {
            raiseEvent(this, eventArgs);
        }
    }

    ...
    public void DeleteThis()
    {
        handlers = null
    }
}

raiseEvent() will go on an call a few other methods, one of which will invoke DeleteThis(). When everything ends and the program flow returns back to raiseEvent() at the "}" for the foreach loop, it finds that handler has been modified = null, thus throwing the error of InvalidOperationException.

Some method handling should disable this objectA as part of the functionality - thus Deletethis() MAY be called by client code at some point. To fix this, I had modified from List handlers to just a single Handler object, but I feel that that should be a better way of workaround. Or better way of coding.

Any advice? Thanks in advance!

Was it helpful?

Solution 2

To address the core of your question: The most straightforward way to fix the issue is to assign the list to a local variable before enumerating over it.

class objectA
{    
    public List<Handler> handlers;
    ...
    public void OnActionHappened()
    {
        List<Handler> lh = handlers;

        // TODO: Would probably make sense to check if lh is null here.

        foreach(Handler h in lh)
        {
            h.raiseEvent(this, eventArgs);
        }
    }
    ...
    public void DeleteThis()
    {
        handlers = null;
    }
}

There is really no need to create a copy of the list as suggested elsewhere.

Since you seem to be new to C# programming, let me give you some idea what is going on here.

List<T> is a reference type. Let us assume that you create a new List<T> by calling its constructor:

List<Handler> handlers = new List<Handler>();

Now, executing this statement creates two things in the computer's memory:

  1. The list object itself.
  2. A variable ("handlers") that refers to the list object.

enter image description here

Now, if the computer executes the following line:

List<Handler> lh = handlers;

we end up with something like this:

enter image description here

Finally, if the computer executes the following line:

handlers = null;

the situation looks as follows:

enter image description here

As you can see, this way we maintain a valid reference to the list object via the local list variable "lh" and setting the member variable "handlers" to null doesn't affect the foreach enumeration any longer.

OTHER TIPS

If you use ToArray on the list, you create a copy of its contents and are not dependant on the handler variable itself:

   foreach(Handler h in handlers.ToArray()
        {
            //optional break if you don't want the loop to continue after DeleteThis is called:  if(handlers==null)break;
            raiseEvent(this, eventArgs);
        }

An event cannot be triggered outside the class in which the event is defined. So, if you move handlers outside class A, you can no more trigger events in handlers in the class A.

To work around this issue, put handlers in another class, say class B, and define a public method that triggers the events in the handlers in the class B (in this case, the onActionHappened method). For class A, simply call that public method (onActionHappened) of the class B.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top