以下是基于第1部分的匿名方法部分中的示例的SSCCE Chris Rolliston的优秀Delphi XE2基础书,关于变量的概念 捕获(其中的任何错误完全取决于我)。
它完全符合我的预期,在连续点击后记录666,667,668,669 BtnInvoke按钮。特别是它很好地说明了捕获的版本 btnSetUpClick退出后,局部变量I持续很长时间。
到目前为止一切顺利。我问的问题本身并不是这个代码,而是 什么在Allen Bauer的博客中说:
http://blogs.embarcadero.com/abauer/2008/10/15/38876
现在,我知道最好不要和老板争辩,所以我确信我错过了这一点 他在变量捕获和值捕获之间的区别。以我简单的方式 看看它,我的基于CR的例子通过捕获I作为变量来捕获I的值。
所以,我的问题是,鲍尔先生试图绘制的区别究竟是什么?
(顺便说一下,尽管每天看了太多的SO的Delphi部分超过9个月,但我还是不完全 如果这个q是主题,请清楚。如果没有,我道歉并且我会把它取下来。)
type
TAnonProc = reference to procedure;
var
P1,
P2 : TAnonProc;
procedure TForm2.Log(Msg : String);
begin
Memo1.Lines.Add(Msg);
end;
procedure TForm2.btnSetUpClick(Sender: TObject);
var
I : Integer;
begin
I := 41;
P1 := procedure
begin
Inc(I);
Log(IntToStr(I));
end;
I := 665;
P2 := procedure
begin
Inc(I);
Log(IntToStr(I));
end;
end;
procedure TForm2.btnInvokeClick(Sender: TObject);
begin
Assert(Assigned(P1));
Assert(Assigned(P2));
P1;
P2;
end;
答案 0 :(得分:10)
变量捕获与值捕获非常简单。让我们假设两个匿名方法捕获相同的变量。像这样:
Type
TMyProc = reference to procedure;
var
i: Integer;
P1, P2: TMyProc;
....
i := 0;
P1 := procedure begin Writeln(i); inc(i); end;
P2 := procedure begin Writeln(i); inc(i); end;
P1();
P2();
Writeln(i);
两种方法都捕获了一个变量。输出是:
0 1 2
这是一个变量的捕获。如果捕获的值不是,那么可以想象这两个方法将具有以值0开始的单独变量。并且两个函数都可能输出0.
在您的示例中,您应该想象P1
捕获值41
,P2
捕获值665
。但这不会发生。只有一个变量。它在声明它的过程和捕获它的匿名方法之间共享。只要所有分享它的各方都活着,它就会存在。所有其他人都可以看到由一方对变量进行的修改,因为只有一个变量。
因此,无法捕获值。要获得您需要将值复制到新变量并捕获该新变量的行为。例如,可以使用参数来完成。
function CaptureCopy(Value: Integer): TMyProc;
begin
Result := procedure begin Writeln(Value); end;
end;
...
P3 := CaptureCopy(i);
这会将i
的值复制到一个新变量,即过程参数,然后捕获它。对i
的后续更改对P3
没有影响,因为捕获的变量是P3
的本地变量。
答案 1 :(得分:5)
让我们澄清一下事情;在内部,匿名方法捕获的任何数据都是隐藏对象实例的字段,应该被称为 variable ;但是可能存在捕获变量的不同情况。
考虑示例代码:
type
TMyProc = reference to procedure;
function CaptureValue(Value: Integer): TMyProc;
begin
Result := procedure begin Inc(Value); Writeln(Value); end;
end;
procedure Test1;
var
Proc1: TMyProc;
I: Integer;
begin
I:= 32;
Proc1:= CaptureValue(I);
Proc1();
Writeln(I); // 32
end;
procedure Test2;
var
Proc2: TMyProc;
I: Integer;
begin
I:= 32;
Proc2:= procedure begin Inc(I); Writeln(I); end;
Proc2();
Writeln(I); // 33
end;
您可以看到Proc1
(Test1
)在I
Proc2
中Test2
的{{1}}内抓取I
的{em>值 }})将引用捕获到I
。
如果你仔细观察,你会注意到Test1
中的变量Proc1
是一个普通的基于堆栈的变量,而Test2
访问一个隐藏对象的字段instance(使用对实例的引用和字段的偏移量);我们有两个不同的变量(一个在堆栈上,另一个在堆上)。
I
根本没有基于堆栈的Test2
变量,只有隐藏对象实例的字段; Proc2
和{{1}}都通过引用实例(以及字段的偏移量)来访问同一个变量;我们有一个堆变量。