是否会首先调用Dispose并在延迟执行时导致失败?

时间:2011-05-24 16:24:20

标签: c# .net linq deferred-execution

有两种方法,其中一种方法在using语句中使用LINQ返回数据。我想知道查询是否有可能抛出某种异常,因为查询执行是延迟的,而它正在使用的变量已经被处理掉了?

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

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

}

顺便说一句,它必须已经以某种形式提出,但我找不到明显的候选人。所以不要太费力地踢我:)

6 个答案:

答案 0 :(得分:4)

我认为如果对象被处理,你会有例外。 This thread非常相似,并提供了几种处理问题的方法。简单的一个是强制执行return d.Select(e => e.Id).ToList(),但可能不适合你

答案 1 :(得分:2)

是的,它可以抛出异常,但这取决于“SomeDisposableSource”的实现。在调用Dispose()之前,您要求源获取IEnumerable或Array,但实际上您在Dispose之后枚举每个元素,因此如果它抛出并且异常与否取决于该“yeld-return”的实际代码码。 (它是否使用任何处置过的物体?)

你可以通过以下方式解决它(具有更高的内存使用率):

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

这样,在执行Dispose()之前,所有枚举都已完成。

编辑:使用:

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

......可能会更好。

答案 2 :(得分:0)

whether it's possible是一个奇怪的问题。对的,这是可能的。您的SomeDisposableSource可能会在GetEnumerator方法中检查是否已将其处理掉。

答案 3 :(得分:0)

我认为Gerardo走在正确的轨道上,但我会对它进行不同的编码,这可能会导致更小的内存占用:

return d.Select(e => e.Id).ToList();
编辑:哎呀! IndigoDelta远远领先于我

答案 4 :(得分:0)

您将(更多)确定性using语句与(不太确定的)LINQ语句混合在一起。通过将资源d包装在using语句中,您明确声明在方法结束时您希望将其处理掉。

因此,如果您希望确保在退出方法之前处置d,则必须立即使用ToArrayToList或某些方式执行LINQ执行其他方法。

更难稍微多一些(每个评论者)路径将创建一个允许资源(IEnumerable<T>)的自定义d使用LINQ语句返回并在以后执行,即调用者现在负责处置IEnumerable<T>(通常只是通过使用foreach块)。

答案 5 :(得分:0)

事实上,您的代码中的执行不会延迟,因为您使用的是常规return。 所以Baz方法执行,返回和处理。稍后,当您枚举结果时,如果此枚举机制依赖于已处置的非托管资源(在您的示例中很可能就是这种情况),则会失败。

解决方法很简单:不要使用return阻止延迟执行,而是使用yield return。这是推迟执行的准确关键字。

你的方法就变成了这个

    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
        }
    }

然后一切都好。在枚举完成之前,using不会调用Dispose方法。