高难度问题 100$(100分)

  • 主题发起人 主题发起人 阿梁
  • 开始时间 开始时间

阿梁

Unregistered / Unconfirmed
GUEST, unregistred user!
本人建立了一个记录类型,如下:
type
RightBar=record
name:string;
age:integer;
btnClick:TNotifyEvent;
end;

建立过程:
procedure Myclick(Sender: TObject);
begin
//do something...
end;

btnClick为TNotifyEvent类型,用于指定指针过程 buttonclick。
要求建立一常量为RightBar型,并对其赋初值。

const aliang:RightBar=(name:'aaa';age:12;btnClick:Myclick);
此时 btnClick:Myclick无法通过编译,
提示错误:Undeclared identifier: 'Myclick'
请问我如何才能在常量中对Btnclick赋初值呢? 望告之谢谢谢谢!!!
 
把 myclick 的声明写在 record type 定义之前。

procedure Myclick(Sender: TObject);

type
RightBar=record
name:string;
age:integer;
btnClick:TNotifyEvent;
end;
 
1、TNotifyEvent必须是一个方法指针。
2、Myclick原型必须在Interface部份加以声明
 
douh ,
您说Myclick原型必须在Interface部份加以声明,请问如何声明?

我现在这样做的,依然不好用!

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
TForm1 = class(TForm)
private
{ Private declarations }
public
procedure Myclick(Sender: TObject);
{ Public declarations }
end;

type
RightBar=record
name:string;
age:integer;
btnClick:TNotifyEvent;
end;

var
Form1: TForm1;
const aliang:RightBar=(name:'aaa';age:12;btnClick:Myclick);
implementation

{$R *.DFM}

{ TForm1 }

procedure TForm1.Myclick(Sender: TObject);
begin
showmessage('dir');
end;

end.

 
别作梦了!我认为这是个基本的OOP编程的原理.

想要使类成员函数地址变成记录的一个分量地址,这是不可能的事,也是完全错误的事!

因为你的Myclick是一个类的成员函数,它的地址是不能附给一个普通的函数变量地址的!
它们根本不兼容!

myclick的地址依赖于self的引用,每次调用myclick时,将隐含的self压栈.
而RightBar.btnClick却是个常量地址(虽然也被标明成TnotifyEvent类型),但却是一个
独立的指针.每次调用时是不会有self压栈的(这里根本不存在self).

试想一下:

即使能够强行赋值,当发出调用RightBar.BtnClick(self)时,由于没有一个隐含的self指针压栈,
函数里还傻瓜一般地假定有个self压在栈上会出什么事?

几乎是必错无疑!
 
对,这样写应该可以(但我觉得不好)

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;

type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;

procedure Myclick(Sender: TObject);

type
RightBar=record
name:string;
age:integer;
btnClick:TNotifyEvent;
end;

var
Form1: TForm1;
const aliang:RightBar=(name:'aaa';age:12;btnClick:Myclick);
implementation

{$R *.DFM}

{ TForm1 }

procedure TForm1.Myclick(Sender: TObject);
begin
showmessage('dir');
end;

end.
 
补充一点.
如果按照下述方法
var
rec: RightBar;
begin
@rec.btnClick :=@TForm1.Myclick;
rec.btnClick (nil);
即能骗过编译器且能似乎正确地编译调用
何故?

那只是碰巧!因为程序中都没引用TForm1中的东东.

我举出下例来说明self的缺少,导致Nil指针的出现.

//能骗过编译器强制成员函数指针到一般函数指针的例子

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure myclick(Sender:TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
s:string;
end;
RightBar=record
name:string;
age:integer;
btnClick:Tnotifyevent;
end;

procedure test(rb:rightbar);这是一个普通的函数

var
Form1: TForm1;

implementation

{$R *.DFM}
procedure test(rb:rightbar);
begin
rb.btnClick (nil);
end;

procedure TForm1.MyClick(Sender: TObject);
begin
showmessage(s);//这句要么非法操作要么根本不是'OK'
end;

procedure TForm1.Button1Click(Sender: TObject);
var
rec: RightBar;
begin
@rec.btnClick :=@TForm1.Myclick;
test(rec);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
s:='ok';
end;

end.

 
jr兄,谢谢你,教俺学了一招。

难道我真的不能用常量了吗???
由于此类型很多,如果用变量的话,我需要一个一个的声明,赋值,声明引用,很麻烦的!!!
jr兄有没有好的办法,能够简化工作,定重谢!
 
可以用过程变量指针.

type
TVMyclick=procedure Myclick(Sender: TObject);
在程序中

var
Vmyclick:TVMyclick;
然后在记录类型里就可以直接用了 。
 
接受答案了.
 
这种赋值是可以的,不过应该使用对象实例方法,而不是对象类的方法,例如
aliang.btnClick:=Form1.Myclick;
所以,我想如果能够保证Form1在aliang前实例化,那么下面的语句是可以的
const aliang:RightBar=(name:'aaa';age:12;btnClick:Form1.Myclick);
 
后退
顶部