2010年6月

(转)Delphi属性Property学习应用总结

Property是一个很有意思的语法特性,它使得方法具有了字段的调用特征,并赋予字段执行动作的能力。

如果你使用过C++ Builder来写基于VCL的程序,我想很多人都会注意到这样一点,比如:
edt.Text = "test"; 这个时候edt文本框的内容会随之改变,但是“理论”上Text应该只是改变了Text所在的内存数据而已,为什么会导致窗口更新界面着一些列动作?

这就是因为Property这一特性,Property是Borland为C++扩展的语法特性,目的在于使C++ Builder能够方便的使用VCL库,毕竟VCL是使用Object Pascal写的。

有些扯远了,言归正传,这里还是总结一下在Delphi中如何使用Poperty这一语法特性。

如果学习过C#的朋友应该会很容易理解,因为C#的属性就是学习自Delphi,毕竟C#和Delphi是同一个设计者。

定义一个属性Property的基本格式如下:
property 属性名 : 属性值类型 read 属性读函数/属性值变量 write 属性写函数/属性值变量

这里简单解释一下:
1。Property是属性定义关键字。

2。属性的特征类似于字段,所以属性名就像字段名,属性值类型就像字段的值类型

3。属性读函数,是属性被“读取”时所执行的操作,这样在执行“取值”操作时,具备了执行其他动作的可能。
另外,属性值变量,可以是Property所在类能够访问的任何变量,如果使用了属性值变量,则相当于属性值直接从值变量中获取,这和直接赋值是没有什么差别的

4。属性写函数,是属性被“写入”时所执行的操作,这样在执行属性“赋值”操作时,具备了执行其他动作的可能。比如:写入edt的Text属性时,窗口会同时执行界面更新操作。
另外,属性值变量和3中所述类似,如果使用了,就相当于将传来的属性值直接赋值到对应的属性值变量

这里的函数是真正的函数,不像C#中那样的getter和setter,所以会有些难以理解。

5。属性读函数的函数声明:
function 读函数名: 属性值类型;

其中读函数名可以自定义,只要和属性声明中一样即可,该函数的返回值就是读属性操作时实际获取的值。

6。属性写函数声明(其实是一个子函数):
procedure 写函数名(value : 属性值类型)

其中写函数名可以自定义,只要和属性声明中一样即可,该函数参数value,就是对属性赋值时传递过来的实际值。

7。读函数和写函数必须设置一个,如果只设置读函数,而没有设置写函数(同时去掉write关键字),这样的属性就是只读属性,同理也可以设置只写属性

8。为了保证属性公开性的同时掩盖读写函数的可见性,可以将读写函数设置为私有,而将属性设置为共有,这样可以避免将读写函数本身暴露给调用者,否则就不太拉风了。

举例:

TxKernelSearchThread = class(TThread)
   private
    keyword_list: TStringList;
     procedure SetKeyword(value: UnicodeString);
   public
    property Kerword:UnicodeString write SetKeyword;
    。。。
end;

procedure TxKernelSearchThread.SetKeyword(value: UnicodeString);
begin
  if value = '' then Exit;
  ExtractStrings([' '],[' '],PWideChar(value),Self.keyword_list);
end;

上面是一个只写属性的例子。

属性的一个很重要的应用就是VCL中的控件属性,以及事件属性等,这也就解释了为什么向文本框的Text属性赋值,会更新界面操作,这正是因为属性将字段和函数的特征结合了起来。

Delphi中使用TThread进行多线程开发总结

最近项目的Demo开发使用Delphi 2010,其中使用了多线程开发技术

这里采用VCL的TThread类完成多线程开发,直接使用CreateThread的办法之前用过很多,这里就当练习,试试采用TThread类

TThread类类似VC中的CThread,主要是采用面向对象思想对CreateThread封装了一层,方便多线程的代码编写,线程执行所需要的相关参数可以很方便的设置,线程的操作和线程状态的获取也很方便。至于内部原理,有兴趣的话可以看看VCL源代码,这里就不深入探讨了。

所使用TThread完成多线程开发,可以按照如下步骤进行:

1。从TThread派生一个类,例如TxDerivedThread,并重载Execute函数,多线程的核心代码主要就在该函数中

2。如果在线程执行中需要设置相关执行参数,则可以在派生类TxDerivedThread中添加字段,并使外部调用者有办法修改,比如使用公共字段,设置Setter函数,或者公共Property。这些字段在Execute函数中是可以访问到的,这样就达到了对函数设置执行参数的目的。

但是因为在构造线程类对象时,线程默认会自动执行,而不会挂起,这样外界就没有了设置参数的时机,所以如果需要设置线程执行参数,则需要重载构造函数Create,在其中调用TThread的构造函数,并传递参数True,使线程创建后立即挂起,然后设置相关参数,最后再将线程Resume即可,这样外部就能够控制线程的创建了

例如如下的派生类:

TxKernelBuildThread = class(TThread)
private
    main_form: HWND; // 主界面窗口句柄
    procedure SetMainForm(value: HWND);
protected
    procedure Execute; override;
。。。
public
    property MainWnd: HWND write SetMainForm;
。。。
end;

constructor TxKernelBuildThread.Create(suspend: Boolean);
begin
    inherited Create(suspend);
end;

在构造时调用者将suspend传递为True,这样线程在创建之初就会挂起,调用者就有机会设置参数了

例如如下的字段设置函数:

procedure TxKernelBuildThread.SetMainForm(value: HWND);
begin
    if value = 0 then
    begin
        Exit;
    end;
    Self.main_form:=value;
end;

在线程创建之后,就可以设置相关的参数,并在线程执行过程中使用:

例如:

procedure TxKernelBuildThread.Execute;
var
fmt: UnicodeString;
params: UnicodeString;
ret: Integer;
begin
   PostMessage(main_form,THREAD_INDEX_START,Self.id,0);
。。。
end;

这样就完成了线程执行参数的传递工作。

至于线程执行结果的获取,也可以采用类似的方法。

3。如果要控制线程的执行,比如挂起,恢复,终止;或者获取线程的状态,比如是否完成,是否挂起等,都有相关的封装函数。

操作函数:Suspend;Resume;Terminate;等

状态获取:ReturnValue;Terminated;Finished;Suspended;等

其他详细函数,可以查看CDN(类似于MSDN,Embarcadero确实比Borland勤快很多了,呵呵)

4。其他的线程同步操作,主要也就是临界区,互斥体,信号量等,这些就不讨论了,比较基本,以后有机会再补上吧。