在 Delphi1.0的user's guide 中 用到 Sender这个通用的prarmeter有时要用sender as ClassRef 的形式
有时只用Sender就可以, 如
with Sender as TButtondo
begin
Caption := '&Ok';
OnClick := OkClick;
end;
if Sender = Button1 then
AboutBox.Caption := 'About ' + Application.Title
else
AboutBox.Caption := ;
也有人写成
case TButton(sender).Tag of
以上这三种形式到底适用在什麽情况下呢?
这是个观念的问题, 我尝试解释看看, 不对与不足的地方还请其他网友不吝指正:
(Sender as TButton) 与 TButton(Sender) 都是 Typecasting,只是语法不同罢了, 因此, 写成 (Sender as TButton).Caption := 'Test';或者 TButton(Sender).Caption := 'Test';
结果都一样
针对个别物件个别事件撰写事件处理程序, 并不需判定 Sender为何, 因为很明
显的 Sender
便是这个正在撰写的物件, 但是在多个物件共用一个事件处理程序的情况下,
Sender 是谁(谁发生OnClick
事件)的判断就有其必要性
if Sender = Button1 的写法是直接判定Sender是不是 Button1这个物件, 但
是如果按钮有 64 个, 写 64 个 if
叙述恐怕会累死人的...
因此, 以类别的判定取代个别物件的副本(instance)的判定, 应是比较简明的
作法, 於是, 我们可以写成
Sender is TButton, 为真时, 以上述型别转换的方式 ---
TButton(Sender).XXXX 撰写, 就可以大辐简化程式,
毕竟,同属 TButton 的物件, 都有相同的属性(属性值不一定相同), 不是吗?
值得注意的是, Sender is (Class Name) 的 Class Name 判断是会牵涉到父阶
的继承关系的, 例如: Button1 是
Button,Button2是 TBitBtn, 这样的话:Button1 is TButton 为真, Button2
is TButton 也是真, 因为TBitBtn 继承自
TButton, 也就是说, 球是球, 篮球也是球. 应用这个观念, 下列的程式:
if ActiveControl is TDBEdit then
(ActiveControl as TDBEdit).CutToClipboard
else
if ActiveControl is TDBMemo then
(ActiveControl as TDBMemo).CutToClipboard;
如果改成:
if ActiveControl is TCustomMemo then
TCustomMemo(ActiveControl).CutToClipboard;
程式的执行效率就更好了, 因为 TDbEdit 与 TDbMemo 的共同父阶是
TCustomMemo, 而 TCustomMemo
也有 CutToClipboard 方法
此外, (Sender as TButton) 在型别转换时若发生错误,会举发例外讯息, 而
TButton(Sender)则强制转型不会举发例外报告. 像是下例的写法,虽然没有例
外讯息,
但在逻辑上毕竟是不正确的;
换句话说, 语法错误是没有, 但潜藏的错误仍然
危险:
// 假设 Sender 事实上是 TButton 型的物件
TPanel(Sender).BevelWidth := 2;
所以, 有一些人主张保险起见应该写成:
// 假设 Sender 事实上是 TButton 型的物件
(Sender as TPanel).BevelWidth := 2;
以便在不正确型别强转换时提示错误的发生.
但话说回来了, 型别转换到底合不合理是程式发展人员的责任, 将 TButton转
型成TPanel就说不通.
不管用哪一种写法, 在转换前作一下判断是应该的,所以, 您可能会常看到类似
如下的写法:
if ActiveControl is TCustomMemo then
TCustomMemo(ActiveControl).CutToClipboard;
用意即在先作一次判断. 当然, 交给例外处理机制接管也行, 但毕竟是打扰了
程式的执行流程,
有点令人讨厌, 我是认为 Except 应该是在「承认理性是局部的」与「程式可
读性」等两个前提下的後援,
可能的话, 能在事前加以判断是比较好的.
准此, 则下列的写法就很经典了:
if Sender is TPanel then
(Sender as TPanel).BevelWidth := 2;
面向对象的编程工具的特点之一就是要提高代码重用性(Reuse),作为新一代可视化开发工具,Delphi中的代码重用性相当高。我们知道,在Delphi中,大部分程序代码都直接或间接地对应着一个事件,此程序称为事件处理句柄,它实际上就是一个过程。从应用程序的工程到表单、构件和程序,Delphi强调的是其开发过程中每一层次的重用性,可以通过编写某些构件常用的事件处理句柄来达到程序重用目的。你可以在属性窗口的Events页上将A事件的处理句柄指向B事件的处理句柄,这样A事件和B事件就共享了一个过程段,从而达到了重用的目的。如果共享的程序段与发生该事件的控件无关,如ShowMessage(′hello,world′),那这种共享是最简单的。但一般来说,代码段间的共享都跟发生该事件的控件有关,需要根据控件类型做出相应的处理,这时就要用到Sender参数。
每个过程段的开头都类似procedure TForm1?FormClick(Sender:TObject);其中的Sender是一个TObject类型的参数,它告诉Delphi哪个控件接收这个事件并调用相应的处理过程。你可以编写一个单一的事件处理句柄,通过Sender参数和IF…then
…语句或者CASE语句配合,来处理多个构件。发生事件的构件或控件的值已经赋给了Sender参数,该参数的用途之一就在于:可以使用保留字IS来测试Sender,以便找到调用这个事件处理句柄的构件或控件的类型。例如,将表单中编辑框和标签的Click事件的处理句柄都指向表单的xxx过程,编辑框和标签对Click事件有不同的反应:
procedure TForm1?xxx(Sender:TObject);
begin
if(sender if Tedit) then
showmessage(′this is a editbox′);
if(sender is Tlabel) then
showmessage(′this is a label′);
end;
Sender参数的第二个用途是结合AS操作符进行类型转换,将若干个派生于某一父类的子类强制转换成该父类。例如表单中有一个TEdit类控件和一个TMemo控件,它们实际上都派生于TcustomEdit类,如果你要为二者的某一事件提供同样处理,可以将二者事件句柄都指向自定义的过程yyy:
Procedure TForm1.yyy(Sender:TObject);
begin
(sender as TcustomEdit).text:=′This is some demo text′;
end;
在过程中,AS操作符将TEdit类和TMemo类均强制转换成TcustomEdit类,再对TcustomEdit类的属性赋值。注意这种转换必须符合Delphi中类的层次关系。
使用Sender参数可以通过单一过程段处理多类控件,真正体现了Delphi面向对象的重用性。