Search code examples
c#eventsdelegatesdeferred-execution

How to defer execution of an Event on each item in a collection until iteration of collection is complete using delegates and/or events?


Of Note: This is more of a curiosity question than anything else.

Given a List<Window> where each window has an event attached to the Close Event which removes the window from the collection, how could you use delegates / events to defer the execution of the Close Event until the collection has been iterated?

For example:

public class Foo
{

    private List<Window> OpenedWindows { get; set; }

    public Foo()
    {
        OpenedWindows = new List<Window>();
    }

    public void AddWindow( Window win )
    {
        win.Closed += OnWindowClosed;
        OpenedWindows.Add( win );
    }

    void OnWindowClosed( object sender, EventArgs e )
    {
        var win = sender as Window;

        if( win != null )
        {
            OpenedWindows.Remove( win );
        }
    }

    void CloseAllWindows()
    {
        // obviously will not work because we can't 
        // remove items as we iterate the collection 
        // (the close event removes the window from the collection)
        OpenedWindows.ForEach( x => x.Close() );

        // works fine, but would like to know how to do
        // this with delegates / events.
        while( OpenedWindows.Any() )
        {
            OpenedWindows[0].Close();
        } 
    }

}

Specifically, within the CloseAllWindows() method, how could you iterate the collection to call the close event, but defer the event being raised until the collection has been completely iterated?


Solution

  • Presumably you're trying to avoid the "Collection was modified" exception in the first case.

    Really the only way to "defer" this is to make a copy of the collection, which involves iterating the original collection fully and adding the items to a new collection:

    var openedWindowsCopy = new List<Window>(OpenedWindows);
    foreach (var window in openedWindowsCopy)
        window.Close();
    

    Also, your second method is valid, although typically when you need to clean up instances in an indexed collection such as a List<T>, you do it simply by counting down:

    for (int i = OpenedWindows.Count - 1; i >= 0; i--)
        OpenedWindows[i].Close();
    

    Personally, I see no reason to complicate the issue with more delegates.