tags:

views:

44

answers:

1

Any efficient/reliable way to expose one event?

I have a class, MultipleDocumentCopier that copies multiple documents thru an instance of SingleDocumentCopier. SingleDocumentCopier exposes an event CopyCompleted that is fired when a file is copied.

Suppose that, I am copying 10 files, instead of raising SingleDocumentCopier.CopyCompleted 10 times, I would like to expose an event, MultipleDocumentCopier.MultipleCopyCompleted.

But is there a standard way/technique to combine multiple events and fire it once?

I would like to raise MultipleDocumentCopier.MultipleCopyCompleted only once
within 'MultipleDocumentCopier.singleDocumentCopier_CopyCompleted', instead of 10 times.

Here is the sample code

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace CombineEvents
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var copier = new MultipleDocumentCopier();
            copier.MultipleCopyCompleted += MultipleDocumentCopyCompleted;
            copier.CopyDocuments(new[] {"File1", "File2", "File3"});
        }

        private static void MultipleDocumentCopyCompleted(
            object sender, FileNameEventArgs e)
        {
            Debug.Print("Following documents have been copied");
            foreach (var fileName in e.FileNames)
            {
                Debug.Print("\t\t\"{0}\"", fileName);
            }
        }
    }

    internal class SingleDocumentCopier
    {
        public event EventHandler CopyCompleted;
        protected virtual void OnCopyCompleted()
        {
            if (CopyCompleted != null) CopyCompleted(this, EventArgs.Empty);
        }

        public void Copy(string fileName)
        {
            Debug.Print("Copying = '{0}'", fileName);
            OnCopyCompleted();
        }
    }

    public class MultipleDocumentCopier
    {
        public event EventHandler<FileNameEventArgs> MultipleCopyCompleted;

        protected virtual void OnCopyCompleted(FileNameEventArgs e)
        {
            EventHandler<FileNameEventArgs> completed = MultipleCopyCompleted;
            if (completed != null) completed(this, e);
        }

        public void CopyDocuments(IEnumerable<string> fileNames)
        {
            var copier = new SingleDocumentCopier();
            copier.CopyCompleted += singleDocumentCopier_CopyCompleted;
            foreach (var fileName in fileNames)
            {
                copier.Copy(fileName);
            }
        }

        public static void singleDocumentCopier_CopyCompleted(
            object sender, EventArgs e)
        {
            // I want to raise "MultipleDocumentCopier.MultipleCopyCompleted" when
            // all files, `fileNames` in "CopyDocuments" have been copied,
            // not for every file being copied.
        }
    }

    public class FileNameEventArgs : EventArgs
    {
        private readonly List<string> _FileNames;

        public List<string> FileNames
        {
            get { return _FileNames; }
        }

        public FileNameEventArgs(IEnumerable<string> fileNames)
        {
            _FileNames = fileNames.ToList();
        }
    }
}
+1  A: 

Why not call MultipleDocumentCopier.OnCopyCompleted from the end of CopyDocuments, and forget singleDocumentCopier_CopyCompleted entirely?

Or maybe this is pseudocode, and your real code is more complicated? Maybe you could keep a collection of outstanding file names inside MultipleDocumentCopier, and each time the singleDocumentCopier_CopyCompleted is raised, you remove one document from the collection. Once the collection becomes empty you call MultipleDocumentCopier.OnCopyCompleted.

Edit: Re 'is there a standard way?' -- not that I'm aware of in C#; F# has an interesting set of mechanisms for combining events like this, but I assume a change in programming language isn't an option.

Tim Robinson
*"maybe this is pseudocode, and your real code is more complicated?"* - Yes, it's just a pseudocode i came up in 5 minutes and the real code is more complicated in a way that, I can't simply call `MultipleDocumentCopier.OnCopyCompleted`... unfortunately.
Sung Meister
`copier.Copy(fileName);` within `MultipleDocumentCopier.CopyDocuments(...)` spawns a new thread so I can't tell when it finished without `singleDocumentCopier_CopyCompleted;`
Sung Meister
@Tim: Thanks. I just took a different route to expose the event once by having a class variable that simply counts how many events has fired as well as another class variable that merges all file names and pass that when raising the event.
Sung Meister