views:

200

answers:

4

Is it generally a bad idea to pass an IEnumerable across appdomain boundaries?

I ask because with my current understanding of IEnumerable implementations, the enumerator isn't going to be used until the collection is, well, enumerated. When you are crossing appdomain boundaries, particularly involving multiple processes, would this not result in multiple trips across the boundary, one for every item returned? If that is the case, then returning the collection in its entirety, when possible, such as in an array, would be preferable in terms of performance, would it not?

+1  A: 

First, it depends how the object to be enumerated is: whether is inherits from MarshalByRef or if it is serializable. In the second case, a copy is passed to the other appdomain, which then resembles the array approach. On the other hand, if it inherits from MarshalByRef, it pretty much depends how the enumerator is accessing the owner instance.

So in general, I'd say that you should only pass IEnumerables accross appdomains if you do know what to expect. Otherwise, you may get unexpected results or bad performance.

Lucero
I should have clarified that I am assuming MBR. Given that, this answer, and the other answers, I think I will avoid IEnumerable return values unless there's a good argument in a specific case.
oakskc
+1  A: 

Yeah, it is a bad idea. An enumerator almost always keeps a reference to the collection it is enumerating. Assuming both are serializable, you'll serialize the entire collection as well when you cross the boundary. No round-trips though.

Hans Passant
A: 

yeah. it's not safe even to pass it across threads. you'd better convert it to array to pass.

Benny
Re "safe" - as long as the threads (or `AppDomain` in this case) are well-behaved, that shoudln't be an issue; but I agree with the arrays...
Marc Gravell
+1  A: 

Actually, assuming MarshalByRefObject, it is 2 trips per item (plus one); one to MoveNext(), and one to Current (for every MoveNext() that returns true). Plus a GetEnumerator() call, and probably a Dispose(). So for a MarshalByRefObject, no: don't do this; use an array.

However, if it isn't MarshalByRefObject it is more interesting. For example, ADO.NET Data Services exposes data on a LINQ API (IQueryable<T> : IEnumerable<T>), but this works by building a specific query when needed, doing one round trip, and iterating back at the client.

Of course, you probably shouldn't be using remoting over any real distance (prefer WCF etc), so maybe the first scenario isn't a huge problem - you won't have much latency. Plus you'll either have the same latency issues on the entities (if MarshalByRefObject) or the serialization costs (if not).

Personally, on the very few occasions I use remoting (usually just to allow dll unloading), I have a MarshalByRefObject to represent some kind of service, and serializable entity objects. Perhaps rather predictably for me, I use protobuf-net to minimise the serialization cost.

Marc Gravell