如果延迟执行会先调用Dispose导致失败吗?

发布于 2024-11-09 14:31:50 字数 446 浏览 5 评论 0原文

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

顺便说一句

,肯定已经以某种形式被问过,但我找不到明显的候选人。所以别踢我太狠了:)

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 :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

人间不值得 2024-11-16 14:31:50

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

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

挽容 2024-11-16 14:31:50

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

您可以通过执行以下操作来解决它(使用更高的内存使用量):

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

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

编辑:使用:

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

...可能会更好。

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.

累赘 2024-11-16 14:31:50

是否可能是一个奇怪的问题。是的,这是可能的。您的 SomeDisposableSource 可能会在 GetEnumerator 方法中检查它是否已被释放。

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.

披肩女神 2024-11-16 14:31:50

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

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

编辑:哎呀! IndigoDelta 远远领先于我

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

世界如花海般美丽 2024-11-16 14:31:50

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

因此,如果您想确保在方法退出之前释放 d,则必须使用 ToArrayToList< 立即执行 LINQ /code> 或类似的其他方法。

更难稍微复杂一点(每个评论者)路径是创建一个自定义 IEnumerable,它允许资源 (d) 与 LINQ 语句一起返回并稍后执行,即调用者现在负责处置 IEnumerable(通常只需通过使用 foreach 块)。

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).

落花浅忆 2024-11-16 14:31:50

事实上,代码中的执行并没有延迟,因为您使用了常规的返回
因此,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
        }
    }

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文