如果延迟执行会先调用Dispose导致失败吗?
有两种方法,其中一种在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我认为如果对象被处置,你会有一个例外。 此线程非常相似,并给出了处理该问题的几种方法。最简单的方法是通过执行
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是的,它可以抛出异常,但这取决于“SomeDisposableSource”的实现。您要求源在调用 Dispose() 之前获取 IEnumerable 或数组,但实际上您是在 Dispose 之后枚举每个元素,因此它是否抛出异常取决于该“yeld-return”的实际代码代码。 (它是否使用任何已处置的对象?)
您可以通过执行以下操作来解决它(使用更高的内存使用量):
这样,所有枚举都会在执行 Dispose() 之前完成。
编辑:使用:
...可能会更好。
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:
That way, all the enumeration is finished before you the Dispose() is executed.
EDIT: Using:
...may be better.
是否可能
是一个奇怪的问题。是的,这是可能的。您的SomeDisposableSource
可能会在GetEnumerator
方法中检查它是否已被释放。whether it's possible
is kind of strange question. Yes, it is possible. YourSomeDisposableSource
may check whether it was disposed or not inGetEnumerator
method.我认为 Gerardo 走在正确的轨道上,但我会对其进行稍微不同的编码,这可能会导致更小的内存占用:
编辑:哎呀! 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:
EDIT: Oops! IndigoDelta is way ahead of me
您将(更)确定性的
using
语句与(不太确定性的)LINQ 语句混合在一起。通过将资源d
包装在using
语句中,您明确声明在方法结束时您希望将其释放。因此,如果您想确保在方法退出之前释放
d
,则必须使用ToArray
、ToList< 立即执行 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 resourced
in thatusing
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 withToArray
,ToList
, or some other method of that variety.The
much harderslightly more involved (per commenter) path would be to create a customIEnumerable<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 theIEnumerable<T>
(usually simply through using aforeach
block).事实上,代码中的执行并没有延迟,因为您使用了常规的
返回
。因此,
Baz
方法会执行、返回并释放。稍后,当您枚举结果时,如果此枚举机制依赖于已释放的非托管资源(这很可能是您的示例中的情况),则该枚举将会失败。解决方法很简单:不要使用
return
来阻止延迟执行,而是使用yield return
来代替。这是延迟执行的准确关键字。你的方法就变成这样了
,然后一切就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 ayield return
instead. It's the accurate keyword to make deferred execution.Your method becomes this
and then all is ok. The
using
doesn't invoke theDispose
method before the enumeration is complete.