News

 

If you see IEnumerable<T> as read only collection crippled brother, this post is for you.

Many times I found myself loosing couple of hours looking for a bug everywhere, but not in a place where it was. From time to time the bug is caused by my simplified perception of IEnumerable. The thing is, if we do not know the mechanism that serves specific IEnumerable elements, we cannot be sure that each iteration will return equal collections with same objects.

It is not a big deal if it hosts immutable objects. With mutable however, we must be careful.

Let’s analyze following properties:

  1:  public static IEnumerable<StringBuilder> ByArray
  2:  {
  3:      get
  4:      {
  5:          return new StringBuilder[] { new StringBuilder("foo") };
  6:      }
  7:  }
  8:  
  9:  public static IEnumerable<StringBuilder> ByYield
 10:  {
 11:      get
 12:      {
 13:          yield return new StringBuilder("foo");
 14:      }
 15:  }

and code that operates on them:

  1:  var enumerable = ByArray;
  2:  enumerable.First()[0] = 'b';
  3:  Console.WriteLine(enumerable.First());
  4:  
  5:  enumerable = ByYield;
  6:  enumerable.First()[0] = 'b';
  7:  Console.WriteLine(enumerable.First());

This will write 2 lines. Can you predict each of them ? If not, I strongly advise you to run the code.

But that is not all. Analyze those two properties:

  1:  public static IEnumerable<StringBuilder> ByDynamicArray
  2:  {
  3:      get
  4:      {
  5:          return new StringBuilder[] { new StringBuilder("foo") };
  6:      }
  7:  }
  8:  
  9:  public static IEnumerable<StringBuilder> ByStaticArray
 10:  {
 11:      get
 12:      {
 13:          return fooArray;
 14:      }
 15:  }
 16:  private static StringBuilder[] fooArray =
 17:      new StringBuilder[] { new StringBuilder("foo") };
 18:  
 19:  

and their usage:

  1:  ByDynamicArray.First()[0] = 'b';
  2:  Console.WriteLine(ByDynamicArray.First());
  3:  
  4:  ByStaticArray.First()[0] = 'b';
  5:  Console.WriteLine(ByStaticArray.First());

Notice that now we are operating directly on property, not on a local variable like in previous example. This is also important and can cause different output.

So be careful and think twice before writing code that operates on IEnumerable<T>.


 

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