When yield a disposable object with using clause, when is that object goes out of scope, and gets disposed?
For example, the following code:
type
MyDisposableObject = public class(IDisposable)
public
method SayHello;
begin
writeLn('Hello');
end;
method Dispose;
begin
writeLn('Disposed!');
end;
end;
Program = class
public
class method CreateObject: MyDisposableObject;
begin
result := new MyDisposableObject;
end;
class method GetObjects: sequence of MyDisposableObject; iterator;
begin
while true do begin
using obj := CreateObject do yield obj; // Is this legitimate code? when obj goes out of scope?
end;
end;
class method Main(args: array of String): Int32;
begin
writeLn('Begin.');
for o in GetObjects do begin
o.SayHello;
end;
writeLn('End.');
end;
end;
end.
I’d say same if you were to assign it to result and return it from the method. Dispose is called when the using clause ends, with no regard of who else might have references to the object and might be trying to do stuff with it after.
If you run the code (test project attached), you can see:
Dispose is NOT called when the using clause ends (ie, at yield)
Dispose is called at the call site after the using clause ends (ie, inside the caller’s for loop)
This seems “yield” in a iterator construct is a special primitive that extends the scope of the using clause to the caller’s for each loop, and the Dispose will be called upon the enumerator of the foreach loop going out of scope.
Anyway, I am seeking insights or confirmation for this caveat.
@wuping So iterators are a bit tricky as the compiler pulls them apart. Given your code:
class method GetObjects: sequence of MyDisposableObject; iterator;
begin
while true do begin
using obj := CreateObject do yield obj; // Is this legitimate code? when obj goes out of scope?
end;
end;
class method Main(args: array of String): Int32;
begin
writeLn('Begin.');
for o in GetObjects do begin
o.SayHello;
end;
writeLn('End.');
end;
First you get
writeLn(‘Begin.’);
then the foreach calls GetObjects and calls MoveNext on the result (movenext is an internal method generated for the GetObjects)
the using creates the obj := CreateObject
the function completely exits at the yield obj and returns obj
you call o(bj).SayHello
you go to the next iteration, which calls MoveNext again. This should dispose the obj, and restart the while loop, going back to sep 3.