将 _Recordset 结果与 TADOConnection.Execute 函数结合使用

Using _Recordset result with TADOConnection.Execute function

TADOConnection.Execute函数returns一个_Recordset.

为了简单起见,我目前正在使用此代码(1):

V := ADOConnection1.Execute(SQL).Fields[0].Value;

我知道记录集永远不会为空所以不用担心BOF

现在我可以用本地 _Recordset 变量这样写 (2).

var
  rs: _Recordset; 

  rs := ADOConnection1.Execute(SQL);
  V := rs.Fields[0].Value;

多一点代码。

现在我的问题是:由于 _Recordset 是 Execute 函数返回的接口变量,如果我 不使用本地 rs变量(1)?使用我的简化代码 (1) 安全吗?这里会不会有引用计数问题?

我想了解一下这个问题。


编辑: 我的问题是针对具体案例的:

V := ADOConnection1.Execute(SQL).Fields[0].Value

我没有对 _Recordset.

的局部变量引用

试试这个:创建一个包含单行的过程

  V := AdoConnection1.Execute(Sql).Fields[0].Value;

,在 运行 应用程序上放置一个断点并查看反汇编。您会在

行之前看到它
jmp @HandleFinally

有 3 个调用

call @IntfClear

那是编译器释放了它必须访问的三个接口才能执行语句,即

  • AdoConnection1.Execute()返回的RecordSet接口,
  • 该 RecordSet 的 Fields 接口,以及
  • 通过 Fields[0] 获得的特定 Field 接口。

因此,它已经自动生成了执行源语句后释放这些接口所需的代码。

以下是一个不完美的类比,但它的反汇编更容易理解;它说明了编译器自动生成的代码来处理最终确定的接口。

给出

type
  IMyInterface = interface
    function GetValue : Integer;
  end;

  TMyClass = class(TInterfacedObject, IMyInterface)
    function GetValue : Integer;
    destructor Destroy; override;
  end;

  TForm1 = class(TForm)
    [...]
    procedure Button1Click(Sender: TObject);
  end;

destructor TMyClass.Destroy;
begin
  inherited;
end;

function TMyClass.GetValue: Integer;
begin
  Result := 1;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  I : IMyInterface;
begin
  I := TMyClass.Create;
  Caption := IntToStr(I.GetValue);
end;

Button1Click 的 CPU 反汇编如下所示

尽管有源代码,红色箭头线是界面被清除的地方 没有做任何明确的事情来做到这一点。在

上放置一个断点
inherited

in TMyClass.Destroy 你会发现它也被调用了,尽管 源代码未明确调用它。

正如我所说,以上是一个不完美的类比。一件有趣的事情是,对于可怕的(从使用 "with" 构造的 pov)替代

procedure TForm1.Button1Click(Sender: TObject);
begin
  with IMyInterface(TMyClass.Create) do
    Caption := IntToStr(GetValue);
end;

它不使用(显式)局部变量,编译器生成 与所示反汇编代码完全相同的 代码。

当然,q 中的情况略有不同,因为分配给记录集对象的内存位于 Ado COM 接口的另一侧,因此无法控制该内存是否正确取消分配超出编译器将生成代码以在其接口上调用 _Release。