Remoting IEnumerables

You’re writing this really cool and innovative class to calculate the first hundred thousand natural numbers. You think about the API, and you realize that returning an array of the numbers a) might take a long time to complete, and b) is going to cause memory usage to spike up like mad.

So you decide to stream them instead, returning an IEnumerable<T> instance instead of an array. Being a crack C# developer, you use the incredibly powerful yield keyword, rather than rolling your own implementation of the IEnumerable<T> interface.

   1: class MyCoolMathEngine


   2: {


   3:     public IEnumerable<int> GetFirstHundredThousandNaturalNumbers()


   4:     {


   5:         for (int i = 0; i < 100000; ++i)


   6:             yield return i;


   7:     }


   8: }





People start downloading your class and it becomes so popular that they want you to host it in an external service, as a remote component.



Fine, you say, and you pick .NET remoting to provide remote access. You pat yourself on the back for thinking ahead and using an enumerator model – it scales rather nicely. You write the plumbing code to host the class, and with a victorious smile on your lips, you write a test client that instantiates and accesses the method.



Only to find that it crashes with a System.Runtime.Serialization.SerializationException that says a type that you didn’t even write is not marked serializable.



That’s when it hits you, or at least that’s when it hit me, when I was modifying FinalizeTypeFinder to get it load on a different AppDomain.



Because yield return was used, what actually gets back to the caller is an instance of a compiler generated class that implements IEnumerable<int>, and that autogenerated class is not marked serializable. Nor does it derive from MarshalByRefObject, so there’s no way it can be remoted.



The only real solution I can think of is to write a custom implementation of IEnumerable<T>, like we had to do back in the C# 1.1 days. Yet another instance where compiler magic doesn’t quite work out in a particular scenario, I suppose.