4

有两种方法,其中一种在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);
        }
    }

}

顺便说一句,它一定已经以某种形式被问过了,但我找不到明显的候选人。所以不要用力踢我:)

4

6 回答 6

4

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

于 2011-05-24T16:32:52.557 回答
2

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

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

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

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

编辑:使用:

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

...可能更好。

于 2011-05-24T16:33:33.547 回答
0

whether it's possible是个奇怪的问题。是的,有可能。您SomeDisposableSource可以检查它是否在GetEnumerator方法中被处理。

于 2011-05-24T16:29:46.557 回答
0

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

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

编辑:哎呀!IndigoDelta 遥遥领先

于 2011-05-24T16:36:49.270 回答
0

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

因此,如果您想确保d在方法退出之前处理它,您必须使用 、 或其他某种方法使 LINQ 立即ToArray执行ToList

稍微复杂一点(每个评论者)的路径是创建一个自定义IEnumerable<T>,允许资源(d)与 LINQ 语句一起返回并在以后执行,即调用者现在负责处理IEnumerable<T>(通常只是简单地通过使用foreach块)。

于 2011-05-24T16:55:09.033 回答
0

事实上,由于您使用常规的return. 因此该Baz方法执行、返回和处置。稍后,当您对结果进行枚举时,如果此枚举机制依赖于已释放的非托管资源(这很可能是您的示例中的情况),这将失败。

解决方法很简单:不要使用 a 来阻止延迟执行,return而是使用 a 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

于 2011-05-24T18:22:51.490 回答