Question

There are two methods, one of which returns a data using LINQ within a using statement. I wonder if it's possible for the query to throw some sort of an exception because the query execution is deferred and a variable it's using has already been disposed?

class Foo
{
    void Bar()
    {
       var bazResult = Baz();
       //... use bazResult here...
    }

    IEnumerable<int> Baz()
    {
        using (var d = new SomeDisposableSource())
        {
            return d.Select(e => e.Id);
        }
    }

}

BTW, it must have been asked already in some form but I can't find the obvious candidate. So don't kick me too hard :)

Was it helpful?

Solution

I think you will have an exception if the object is disposed. This thread is very similar and gives a couple of methods for handling the problem. The simple one is to force execution by doing a return d.Select(e => e.Id).ToList() but that might not be suitable for you

OTHER TIPS

Yes, it can throw an exception, but it depends on the implementation of "SomeDisposableSource". You are asking the source to get an IEnumerable or an Array before calling the Dispose(), but you are actually enumerating each element after the Dispose, so if it throws and exception or not depends on the actual code for that "yeld-return" code. (does it use any disposed objects?)

You can work around it, (with higher memory usage) by doing:

return d.Select(e => e.Id).ToArray();

That way, all the enumeration is finished before you the Dispose() is executed.

EDIT: Using:

return d.Select(e => e.Id).ToList();

...may be better.

whether it's possible is kind of strange question. Yes, it is possible. Your SomeDisposableSource may check whether it was disposed or not in GetEnumerator method.

I think Gerardo is on the right track but I would code it a bit differently which might result in a smaller memory footprint:

return d.Select(e => e.Id).ToList();

EDIT: Oops! IndigoDelta is way ahead of me

You're mixing up the (more) deterministic using statement with a (less deterministic) LINQ statement. By wrapping the resource d in that using statement you are explicitly stating that by the end of the method you would like it to be disposed.

Therefore if you would like to ensure d is disposed prior to the exit of the method, you'll have to make the LINQ execution immediate with ToArray, ToList, or some other method of that variety.

The much harderslightly more involved (per commenter) path would be to create a custom IEnumerable<T> which allowed the resource (d) to be returned with the LINQ statement and be executed at a later time, i.e. the caller is now responsible for disposing the IEnumerable<T> (usually simply through using a foreach block).

In fact the execution is not deferred in your code because you use a regular return. So the Baz method executes, returns and dispose. Later on, when you'll enumerate on the result, if this enumeration mechanism relies on unmanaged resources which have been disposed (which is most likely the case in your sample), this will fail.

The workaround is simple : don't prevent the deferred execution with a return but use a yield return instead. It's the accurate keyword to make deferred execution.

Your method becomes this

    IEnumerable<int> Baz()
    {
        using (var d = new SomeDisposableSource())
        {
            //return d.Select(e => e.Id); //Baaaad ! No proper deferred execution
            foreach (var i in d.Select(e => e.Id)) yield return i; //Proper deferred execution
        }
    }

and then all is ok. The using doesn't invoke the Dispose method before the enumeration is complete.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top