gpt4 book ai didi

delphi - 为什么在这个匿名方法中捕获的参数会被重置?

转载 作者:行者123 更新时间:2023-12-02 02:26:39 26 4
gpt4 key购买 nike

以下代码基于本文:http://blog.barrkel.com/2010/01/using-anonymous-methods-in-method.html .

当匿名过程中的事件处理程序代码被触发时(在更改网格中的一行时),第一个“if”报告 dbgrid 不为 nil,但第二个报告为 nil。

知道这里发生了什么吗?您可以从 here 获取完整的源代码(您可能必须更改 TClientDataSet 的 FileName 属性以指向解压缩项目的目录)。

unit Main;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Data.DB, Vcl.DBGrids, Datasnap.DBClient, Vcl.Grids;

type

AfterScrollEventHandler = reference to procedure (sender: TDataSet);

TForm3 = class(TForm)
dbgrdGrid: TDBGrid;
cdsDataSet: TClientDataSet;
dsDataSet: TDataSource;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

procedure MethodReferenceToMethodPtr(const MethRef; var MethPtr);
procedure InjectEventHandler(dataSet: TDataSet; dbGrid: TDBGrid);

var
Form3: TForm3;

implementation

{$R *.dfm}
procedure InjectEventHandler(dataSet: TDataSet; dbGrid: TDBGrid);
var
eventHandlerRef : AfterScrollEventHandler;
eventHandlerPtr : TDataSetNotifyEvent;
begin

eventHandlerRef := procedure (sender: TDataSet)
begin
if dbGrid <> nil then
MessageDlg('1: AfterScroll: The grid is not nil!', mtInformation, [mbOK], 0)
else
MessageDlg('1: AfterScroll: The grid is nil! It''s going to crash...', mtInformation, [mbOK], 0);

if dbGrid <> nil then
MessageDlg('2: AfterScroll: The grid is not nil!', mtInformation, [mbOK], 0)
else
MessageDlg('2: AfterScroll: The grid is nil! It''s going to crash...', mtInformation, [mbOK], 0);

end;

MethodReferenceToMethodPtr (eventHandlerRef, eventHandlerPtr);

dataSet.AfterScroll := eventHandlerPtr;

end;



procedure MethodReferenceToMethodPtr(const MethRef; var MethPtr);
type
TVtable = array[0..3] of Pointer;
PVtable = ^TVtable;
PPVtable = ^PVtable;
begin
// 3 is offset of Invoke, after QI, AddRef, Release
TMethod(MethPtr).Code := PPVtable(MethRef)^^[3];
TMethod(MethPtr).Data := Pointer(MethRef);
end;

procedure TForm3.FormCreate(Sender: TObject);
var
eventHandlerRef1, eventHandlerRef2 : AfterScrollEventHandler;
begin

InjectEventHandler(cdsDataSet, dbgrdGrid)
end;

end.

更新:

此代码按预期工作(匿名过程将 dbGrid 参数存储在本地变量中):

procedure InjectEventHandler(dataSet: TDataSet; dbGrid: TDBGrid);
var
eventHandlerRef : AfterScrollEventHandler;
eventHandlerPtr : TDataSetNotifyEvent;
begin

eventHandlerRef := procedure (sender: TDataSet)
var grid: TDBGrid;
begin
grid := dbGrid;
if grid <> nil then
MessageDlg('1: AfterScroll: The grid is not nil!', mtInformation, [mbOK], 0)
else
MessageDlg('1: AfterScroll: The grid is nil! It''s going to crash...', mtInformation, [mbOK], 0);

if grid <> nil then
MessageDlg('2: AfterScroll: The grid is not nil!', mtInformation, [mbOK], 0)
else
MessageDlg('2: AfterScroll: The grid is nil! It''s going to crash...', mtInformation, [mbOK], 0);

end;

MethodReferenceToMethodPtr (eventHandlerRef, eventHandlerPtr);

dataSet.AfterScroll := eventHandlerPtr;

end;

最佳答案

您看到了未定义的行为。您引用的文章提到方法引用需要在方法指针的生命周期内保持事件状态,但您的代码违反了该规则。与方法引用关联的对象可能会被销毁,因此保存捕获值的 dbGrid 变量不再存在。您正在读取垃圾值,并且内存中的位置可能会在第一次读取和第二次读取之间更改值。我们甚至不知道您读取的值是否等于您期望的值,只是它不为零。

当您使用局部变量时,它似乎可以工作,因为在第一次和第二次读取之间写入的任何内存显然不再与函数期望 dbGrid 或 grid 的位置重叠> 驻留变量。但你仍然在读垃圾。这只是垃圾,碰巧不是 nil 两次而不是一次。

使您的 eventHandlerRef 变量成为封闭类的字段而不是局部变量,一切都应该很好,假设 Delphi 2010 未记录的实现细节在您使用的版本中仍然有效现在。

关于delphi - 为什么在这个匿名方法中捕获的参数会被重置?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23302972/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com