delphi – 如何检查对程序的引用是否为零?

栏目: 编程语言 · 发布时间: 6年前

内容简介:http://stackoverflow.com/questions/29073867/how-to-check-if-a-reference-to-procedure-is-nil

在以下示例代码中,对AssertTestObj()的调用会导致访问冲突.

Project InvokeTest2.exe raised exception class $C0000005 with message  access violation at 0x00000000: read of address 0x00000000’.

调试时,我可以看到TSafeCall<T> .Invoke()中的Assigned(NotifyProc)测试不能按预期的方式运行 – 所以Invoke()会尝试执行NotifyProc,否则会导致访问冲突.

任何想法为什么会失败,如何解决?

program InvokeTest2;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

type
  TSafeCall<T> = class
  public
    type
      TNotifyProc = reference to procedure (Item: T);
    class procedure Invoke(NotifyProc: TNotifyProc; Item: T); overload;
  end;

  TOnObj = procedure (Value: String) of object;

{ TSafeCall<T> }

class procedure TSafeCall<T>.Invoke(NotifyProc: TNotifyProc; Item: T);
begin
  if Assigned(NotifyProc) then
    NotifyProc(Item);
end;

procedure AssertTestObj(OnExceptionObj_: TOnObj; Value_: String);
begin
  TSafeCall<String>.Invoke(OnExceptionObj_, Value_);
end;

begin
  try
    TSafeCall<String>.Invoke(nil, 'works as expected');

    AssertTestObj(nil, 'this causes an access violation!');
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

这是一个编译器错误.这是我简化的复制品:

{$APPTYPE CONSOLE}

type
  TProc = reference to procedure;
  TOnObject = procedure of object;

procedure Invoke(Proc: TProc);
begin
  if Assigned(Proc) then
    Proc();
end;

procedure CallInvokeOnObject(OnObject: TOnObject);
begin
  Invoke(OnObject);
end;

begin
  Invoke(nil); // succeeds
  CallInvokeOnObject(nil); // results in AV
end.

你可能会想我为什么简化.你的代码是一个很好的复制问题.不过,我想让它绝对尽可能简单,所以我真的可以确定问题是我认为的.所以我删除了泛型和类.

现在,使用Assigned的测试是正确的.你是对的,期望它会按照你的意图行事.问题是当编译器生成从CallInvokeOnObject调用Invoke的代码时,它需要在引用过程接口中包装对象的方法.为了正确地做到这一点,需要测试对象的方法是否被分配.如果没有,那么不应该创建包装器接口,并且Invoke应该被传递为零.

编译器无法做到这一点.它无条件地将对象的方法包装在引用过程接口中.您可以在为CallInvokeOnObject发出的代码中看到这一点.

Project1.dpr.16: begin // this is the beginning of CallInvokeOnObject
004064D8 55               push ebp
004064D9 8BEC             mov ebp,esp
004064DB 6A00             push $00
004064DD 53               push ebx
004064DE 33C0             xor eax,eax
004064E0 55               push ebp
004064E1 683B654000       push $0040653b
004064E6 64FF30           push dword ptr fs:[eax]
004064E9 648920           mov fs:[eax],esp
004064EC B201             mov dl,$01
004064EE A1F4634000       mov eax,[$004063f4]
004064F3 E8DCDAFFFF       call TObject.Create
004064F8 8BD8             mov ebx,eax
004064FA 8D45FC           lea eax,[ebp-$04]
004064FD 8BD3             mov edx,ebx
004064FF 85D2             test edx,edx
00406501 7403             jz $00406506
00406503 83EAF8           sub edx,-$08
00406506 E881F2FFFF       call @IntfCopy
0040650B 8B4508           mov eax,[ebp+$08]
0040650E 894310           mov [ebx+$10],eax
00406511 8B450C           mov eax,[ebp+$0c]
00406514 894314           mov [ebx+$14],eax
Project18.dpr.17: Invoke(OnObject);
00406517 8BC3             mov eax,ebx
00406519 85C0             test eax,eax
0040651B 7403             jz $00406520
0040651D 83E8E8           sub eax,-$18
00406520 E8DFFDFFFF       call Invoke

对TObject.Create的调用是在引用过程接口中包含对象的方法.请注意,该接口是无条件创建的,然后传递给Invoke.

没有办法在Invoke内部解决这个问题.到代码到达那里的时候太晚了.您无法检测到该方法未分配.这应该报告给Embarcadero作为一个错误.

您唯一可行的解​​决方法是在CallInvokeOnObject中添加额外的分配检查.

http://stackoverflow.com/questions/29073867/how-to-check-if-a-reference-to-procedure-is-nil


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

无懈可击的Web设计

无懈可击的Web设计

西德霍姆 / 刘建宁 / 清华大学出版社 / 2009-4 / 59.90元

一个网站,无论视觉上多么美观,内容多么丰富,如果不能面向最广泛的用户群,那它就不算是真正成功的网站。《无懈可击的Web设计:利用XHTML和CSS提高网站的灵活性与适应性》是Web标准设计领域的公认专家Dan Cederholm的倾力之作,向您描述了基于Web标准的设计策略,以适应各种各样的用户浏览方式。书中每一章的开头都给出了一个基于传统HTML技术的实例,然后对它进行重构,指出它的局限性,并利......一起来看看 《无懈可击的Web设计》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

在线进制转换器
在线进制转换器

各进制数互转换器

随机密码生成器
随机密码生成器

多种字符组合密码