News


I found that Linq’s ZIP is really great for adjacent item computations.

For example, let’s have a collection of dates:

  1:  var dates = new DateTime[]
  2:  {
  3:      new DateTime(2000,1,1),
  4:      new DateTime(2000,1,2),
  5:      new DateTime(2000,1,5)
  6:  };

How would you compute time difference of adjacent items ?

I like to use Zip for this kind of job:

  1:  dates.Zip(dates.Skip(1), (d1, d2) => d2 - d1);

As you might expect, the result will be: { 1 day, 3 days }



Today I faced following problem:

User picks many files by OpenFileDialog control. They must be loaded asynchronously. Loading some of them may fail. I want to receive callback when loading completes. How can I encapsulate all of this in single repository Load method signature ?

After some time I came up with following interface:

  1:      interface IAsyncResult<out T>
  2:      {
  3:          T Result { get; }
  4:          Exception Error { get; }
  5:          bool IsCompleted { get; }
  6:          bool Wait(int timeout);
  7:          void AttachCallback(Action<IAsyncResult<T>> callback);
  8:      }

Similarity to .NET's IAsyncResult is intentional, although my version is generic and simplified.
Instead of supporting AsyncWaitHandle I expose only Wait method.

 

Example

Having IAsyncResult<T> we can define Image repository like this:

  1:      interface IImageRepository
  2:      {
  3:          IEnumerable<IAsyncResult<Image>> LoadImages();
  4:      }

 

 

Implementation

Following implementation wraps synchronous job to IAsyncResult<T> job.
ConcurrentBag was use to synchronize callbacks. Callbacks are stored as weak references to avoid memory leaks.

  1:      public class JobResult<T>
  2:      {
  3:          public T Result { get; set; }
  4:          public Exception Error { get; set; }
  5:      }
  6:  
  7:      public class AsyncResultFromSyncJob<T> : IAsyncResult<T>, IDisposable
  8:      {
  9:          private Func<JobResult<T>> synchronousJob;
 10:          private readonly ManualResetEvent completedEvent =
 11:                  new ManualResetEvent(false);
 12:          private readonly ConcurrentBag<WeakReference> completionCallbacks =
 13:                  new ConcurrentBag<WeakReference>();
 14:  
 15:          private void OnCompletion(IAsyncResult ar)
 16:          {
 17:              var jobResult = synchronousJob.EndInvoke(ar);
 18:              this.Result = jobResult.Result;
 19:              this.Error = jobResult.Error;
 20:              this.completedEvent.Set();
 21:  
 22:              synchronousJob = null;
 23:  
 24:              FireCallbacks();
 25:          }
 26:          private void FireCallbacks()
 27:          {
 28:              WeakReference wr;
 29:              Action<IAsyncResult<T>> callback;
 30:  
 31:              while (completionCallbacks.TryTake(out wr))
 32:                  if ((callback = wr.Target as Action<IAsyncResult<T>>) != null)
 33:                      Task.Factory.StartNew(() => callback(this));
 34:          }
 35:  
 36:          public AsyncResultFromSyncJob(Func<JobResult<T>> synchronousJob)
 37:          {
 38:              this.synchronousJob = synchronousJob;
 39:              synchronousJob.BeginInvoke(OnCompletion, null);
 40:          }
 41:  
 42:          #region IAsyncResult<T>
 43:  
 44:          public T Result { get; private set; }
 45:          public Exception Error { get; private set; }
 46:          public bool IsCompleted { get { return this.completedEvent.WaitOne(0); } }
 47:  
 48:          public bool Wait(int timeout = Timeout.Infinite)
 49:          {
 50:              return completedEvent.WaitOne(timeout);
 51:          }
 52:  
 53:          public void AttachCallback(Action<IAsyncResult<T>> callback)
 54:          {
 55:              completionCallbacks.Add(new WeakReference(callback));
 56:              
 57:              if(IsCompleted)
 58:                  FireCallbacks();
 59:          }
 60:  
 61:          #endregion
 62:  
 63:          #region IDisposable
 64:  
 65:          public void Dispose()
 66:          {
 67:              completedEvent.Dispose();
 68:          }
 69:  
 70:          #endregion
 71:      }

 

I found Mark Seemann's IAsyncResult generic implementation after writing my own:
http://blogs.msdn.com/b/ploeh/archive/2007/02/09/agenericiasyncresultimplementation.aspx