• Delphi中使用比较少的一些语法


    本文是为了加强记忆而写,这里写的大多数内容都是在编程的日常工作中使用频率不高的东西,但是又十分重要。

    ---Murphy

    1,构造和析构函数:

    a,构造函数:

    一般基于TComponent组件的派生类,都应该使用overload关键字进行继承,Delphi中的对象没有什么复合的概念,在设计时,从简便的角度出发

    一般都设计为耦合性较强,但是使用简单的派生类即可。构造函数不是必写的,除非“复合”这样的对象实现,当省略构造函数时,会由其父类来实现

    新对象的建立。下面是几个常用的写法:

    constructor TfmBaseScreen.Create(AOwner: TComponent);  //由于参数并没有变化,所以这里在声明的时候可以加override而不是overload
    begin
      inherited; // Create(AOwner) ; //这里的Create默认是可以省略的。
      if AOwner is TPanel then
      Self.Width :=TPanel(AOwner).Width;
    end;

    //-------------------------

    constructor TfmHuaPianScan.Create(AOwner: TComponent; AQuery: TADOQuery;
      AZDCode: string; AMvOutBillCode : string; MoveOutQuery : TADOQuery);   //这个声明时需要增加overload说明
    begin
      inherited Create(AOwner);
      FQuery := AQuery; 这里实现的仅仅是私有变量的初始化,其实更多的应用是派生类成员对象的初始化,并需要在析构函数中进行释放。
      FZDCode := AZDCode;
      FMvOutBillCode := AMvOutBillCode;
      FMoveOutQuery := MoveOutQuery;
    end;

    //-------------------------

    Constructor TDllLoader.Create(strDLLName : String);  //这是一个基于TObject的类,由于没有任何成员,所以这里Create什么关键字都不需要带。
    Begin

       FhDLL := LoadLibrary(strDLLName);
       ASSERT(FhDLL <> 0);
    End;

    b,析构函数

    析构函数的使用比构造函数要严格一些,一定要在任何时候都使用override,以保证父类内存空间可以正确的释放。

    下面是几个析构函数的写法:

    Destructor TDllLoader.Destroy();   //虽然函数体内没有inherited但是声明时一定要加上override.

    Begin

        If FhDLL <> 0 then 

        begin

            FreeLibrary(FhDLL); 

        End;

    end

    //-----------------------------------------

    destructor TfmTest.Destroy;  //窗体类的析构,一定也要在声明的时候增加override.
    begin
        temp.Free;   //成员的释放应该都安排在inherited之前。
        inherited;
    end;

     c,窗体的创建和摧毁事件与构造析构的关系

    当然,在窗体类的构造和析构当中,有很多成员管理方式,可以直接通过消息-->事件模式来实现,并不需要引用类及对象设计模式。

    直接调用窗体的OnCreate和OnDestroy就可以完成很多事情。

    我们先来看看窗体构造方法的一段代码:

    constructor TCustomForm.Create(AOwner: TComponent);
    begin
        GlobalNameSpace.BeginWrite;
        try
          CreateNew(AOwner);
          if (ClassType <> TForm) and not (csDesigning in ComponentState) then
          begin
            Include(FFormState, fsCreating);
            try
              if not InitInheritedComponent(Self, TForm) then
                raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);
            finally
               Exclude(FFormState, fsCreating);
            end;
            if OldCreateOrder then DoCreate;
          end;
        finally
             GlobalNameSpace.EndWrite;
        end;
    end;

    procedure TCustomForm.AfterConstruction;
    begin
        if not OldCreateOrder then DoCreate;
        if fsActivated in FFormState then
        begin
           Activate;
           Exclude(FFormState, fsActivated);
        end;
    end;

    从以上的VCL源码可以看到Create构造方法和FormCreate 事件执行顺序是可以调配的,当OldCreateOrder为True时,是执行Create时调用OnCreate事件的;

    而OldCreateOrder为False时,是在窗体AfterConstruction中调用OnCreate事件的。

    而OldCreateOrder属性值默认就是False,也就是默认在AfterConstruction中调用。或者说,继承窗口的时候,默认是先执行Create代码段,再执行FormCreate代码段。

     我们再来看看Destroy和FormDestroy的执行顺序,如下是VCL源码:

    procedure TCustomForm.BeforeDestruction;
    begin
          GlobalNameSpace.BeginWrite;
          Destroying;
          Screen.FSaveFocusedList.Remove(Self);
          RemoveFixupReferences(Self, '');
          if FOleForm <> nil then FOleForm.OnDestroy;
          if FormStyle <> fsMDIChild then Hide;
          if not OldCreateOrder then DoDestroy;
    end;

    destructor TCustomForm.Destroy;
    begin
         if not (csDestroying in ComponentState) then GlobalNameSpace.BeginWrite;
         try
             if OldCreateOrder then DoDestroy;
             MergeMenu(False);
             if HandleAllocated then DestroyWindowHandle;
             Screen.RemoveForm(Self); 
             FCanvas.Free;
             FIcon.Free;
             FreeAndNil(FActionLists);
             inherited Destroy;
         finally
             GlobalNameSpace.EndWrite;
         end;
    end;

    同样的析构方法跟FormDestroy事件的执行顺序也是可以调配的,当OldCreateOrder为True时,系统是通过析构Destroy直接调用OnDestroy事件的;

    而当OldCreateOrder为False时,系统是通过BeforeDestruction来调用OnDestroy事件的。

    同Create的一样,OldCreateOrder默认值是False,所以默认就是在BeforeDestruction中调用,也就是说,继承窗体中,先执行OnDestroy事件代码部分,再执行析构。

    2,类方法及引用类

    a,类方法

    实现模式很简单,只需要在定义过程或函数时,增加一个class关键字就可以了,它的地位跟C语言中的静态方法的地位是一样的。它所处理的信息都是与类相关,

    不能引用对象成员。

    下面是TObject中的一个标准的类方法,用来返回一个类名:

    class function TObject.ClassName: ShortString;

    b,类中类(引用类)

    普通类的语法规则是:

    类名=class(父类名)

        成员描述;

    end;

    而类中类的语法是:

    类名=Class of 父类名

        成员描述;

    end;      

    类中类的父类似乎是其一个对象成员,它可以直接调用其父类的类方法,而类中类定义出的对象,其实是一个类。

    我们可以在用类中类定义的对象中,使用构造和析构方法,构造方法可以调用对象的Create也可以调用其类中类的Create,

    但是,析构函数不能使用类中类的类方法,只能使用其创建对象的析构。这种方法常用于将类作为参数的使用上,方便

    在某一个方法之中,对其不明类(但明其祖类)的对象,进行类方法调用。

    下面是一个标准使用的例子,这是Vcl中TApplication所带的方法,虽然名称是创建Form,其实对于任何基于TComponent的

    对象都是可以创建的,其实现手法就是类中类:

    procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
    var
      Instance: TComponent;
    begin
      // Set flag that TCustomForm constructor can read, so it knows if it's being
      // created as a main form or not (required when MainFormOnTaskbar is True)
      FCreatingMainForm := (FMainForm = nil) and InstanceClass.InheritsFrom(TForm);
      Instance := nil;
      try
    {$IF DEFINED(CLR)}
        Instance := InstanceClass.Create(Self);
        Reference := Instance;
    {$ELSE}
        Instance := TComponent(InstanceClass.NewInstance);  //这里是直接用的TComponent对象赋值一个派生类空间,面向对象是允许的
        TComponent(Reference) := Instance;  //这里的Reference是一个实参,这句用于指向创建的对象
        try
          Instance.Create(Self);   //NewInstance创建的空间只是分配一片空白区域,这里将其头部区域格式化成TComponent
        except
          TComponent(Reference) := nil;
          raise;
        end;
    {$IFEND}

        if (FMainForm = nil) and (Instance is TForm) then  //当系统中还没有明确主窗口时,而创建的对象是窗体对象,则在这里将其自动设置为主窗体。
        begin
          TForm(Instance).HandleNeeded;
          FMainForm := TForm(Instance);
          if MainFormOnTaskBar then
            SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or WS_EX_NOACTIVATE);
          ChangeAppWindow(Handle, not MainFormOnTaskBar, not MainFormOnTaskBar);
        end;
      finally
        if (FMainForm = nil) and (Instance is TForm) then
          TForm(Instance).FCreatingMainForm := False;
      end;
    end;

    类方法中的Self,指的是类本身,而不是对象,所以没办法象普通Self那样使用。

        一个类必须创建具有其对象状态的成员,才是有意义的类。在我上一家公司中,见到有人卖弄高深的把全局方法都建立在一个类中,用类实现。

    这样的类,既没有其多样的对象,在方法实现时,甚至一点与类相关的东西都没有,还不如建立一个公共方法单元。

        一个标准的类,也不能仅仅是对一堆封装成员的Get和Set操作,应该在成员的逻辑处理中,简化其应有的方法,又要保留必要的变化细节。

    否则的话,还不如定义一个结构体。

    3,构造自己的异常,及合理应用

    EFileOpenFailed = class(Exception)

        public procedure Warning(); virtual; abstract;  //这里是纯虚函数的定义,在使用时,还必须再定义一层子异常类。

    end;

    所有异常类,都应该以E开头,并且从Exception继承。并且,在触发异常时,应该从继承树的枝节点写起,

    根节点的异常触发应该写到最后。异常的调用,可以在except包含段中以

    [on 异常实例:异常类定义 do]的模式进行调用。再或者,可以用[raise 异常类名.Create(构造参数列表);]来直接抛出异常 

        try

             SimulateError(Button)    

        except

             on E : EFileOpenFailed do

                E.Warning();

             on E : Exception do

                ShowMessage(E.Message);    

        end;

    4,起源于万物之王的消息机制

    Delphi跟Windows消息是一脉相承的,或者说是扩展消息。Windows消息到了Delphi被重新转换成固定的结构体(Record),

    然后,由Delphi的对象进行分发处理,再由消息函数进行接收运行其对应功能。

    对于Delphi,可以说所有的对象都具有消息接收功能,这个需要追溯到对象的最初定义TObject:

      TObject = class
      public
        constructor Create;             //构造函数
        procedure Free;                  //用于释放对象
        class function InitInstance(Instance: Pointer): TObject;  //使用固定地址,格式化一片实例数据,并返回其对象指针。但是这个并不能分配内存,只操作已分配的内存
        procedure CleanupInstance; //清空对象本身所指向的区域内存数据,但是并不释放内存
        function ClassType: TClass; inline;  //返回类类型,这里的inline相当于注释,表示该功能向下兼容,并且可以减少编译文件的大小。
        class function ClassName: string;  //返回类名
        class function ClassNameIs(const Name: string): Boolean; //用类名判断类
        class function ClassParent: TClass; //返回父类,如果强制返回一个基于TObject的父类,则会报错。
        class function ClassInfo: Pointer; inline; //返回类信息的指针
        class function InstanceSize: Longint; inline; //返回基于当前类的实例使用的空间大小
        class function InheritsFrom(AClass: TClass): Boolean; //判断是否继承于特定类
        class function MethodAddress(const Name: ShortString): Pointer; overload; //根据类所含方法的名称,返回对应指针。
        class function MethodAddress(const Name: string): Pointer; overload; //同上
        class function MethodName(Address: Pointer): string; //根据方法的地址指针,返回方法名
        function FieldAddress(const Name: ShortString): Pointer; overload; //根据属性的名称,返回属性的地址。
        function FieldAddress(const Name: string): Pointer; overload; //同上
        function GetInterface(const IID: TGUID; out Obj): Boolean; //获得一个接口地址,返回执行成功与否
        class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry; //直接返回一个接口地址
        class function GetInterfaceTable: PInterfaceTable;  //获取接口表地址
        class function UnitName: string; //类定义所在的单元文件名
        function Equals(Obj: TObject): Boolean; virtual;  //对象比较
        function GetHashCode: Integer; virtual; //得到哈希代码
        function ToString: string; virtual;  //等同ClassName,可多态改动
        function SafeCallException(ExceptObject: TObject;
          ExceptAddr: Pointer): HResult; virtual;  //以安全模式调用异常
        procedure AfterConstruction; virtual;  //构造后处理
        procedure BeforeDestruction; virtual; //析构前预处理
        procedure Dispatch(var Message); virtual; //消息分发
        procedure DefaultHandler(var Message); virtual; //默认接收方法,可以接收所有编号的消息。
        class function NewInstance: TObject; virtual;  //开辟一个新对象空间,其地址是由系统自动分配的
        procedure FreeInstance; virtual;  //释放实例空间
        destructor Destroy; virtual;  //析构
      end;       

     消息使用的三个步骤,

    <1>,给具有消息结构体的消息变量赋值。

    <2>,构造一个具有处理消息方法的类。处理消息方法的书写格式:方法名(var 消息参数); message 消息号;

    <3>,用一个消息处理类的实例,用Dispatch或者DefaultHandler方法对消息进行分发和处理。这里的分发是指分发到类内部过程。

    例程:

      TMyMsg = record  //消息定义,这里是一种最简单的消息结构体。
        Msg : Cardinal;
        MsgText : ShortString;
      end;

      TMsgAccepter = class  //接收消息类定义,这里省略了消息方法阐述部分
      private
        procedure AcceptMsg2000(var msg : TMyMsg); message 2000;  //这里定义触发方式及触发消息号
        procedure AcceptMsg2002(var msg : TMyMsg); message 2002;
      public procedure
        DefaultHandler(var Message); override;  //这里用于接收其他编号的消息
      end;

      //调用过程

    var
      MsgAccept : TMsgAccepter;
      Msg :TMyMsg;
    begin
      MsgAccept := TMsgAccepter.Create;
      try
        Msg.Msg:=2000;
        Msg.MsgText:='消息测试文本';
        MsgAccept.Dispatch(Msg);   //TObject自带的方法
      finally
        MsgAccept.Free;
      end;
    end;

    VCL的事件定义都是如此,先在VCL控件中定义出接收特定消息的方法,再由事件属性(这里的属性是一个指针),去指定相关方法,

    并在事件(TNotifyEvent)子类中形成用户事件区。

    注意:以WM_开头的消息,是Windows定义的消息;以CM_开头的消息,是VCL库自定义的消息。

     5,行标的定义Label.

    面向对象的编程中并不建议使用行表和goto跳转语句,所以在通常的Delphi代码中很少见,但是并不是说Delphi不具备goto跳转,eg.

    var
     a,b: Integer;
    label
     X,Y;
    begin
     if a > b then
      goto X
     else
      goto Y;
    X:
     WriteLn('a>b');
    Y:
     WriteLn('b>a');
    end;

    6,一个几乎脱离VCL的程序框架:

    program Console;
    {$APPTYPE CONSOLE} //这里表示支持对用户控制台的操作
    //uses SysUtils;
    var
      str:string;
    begin
      writeln('您好,这是一个示范程序,请输入一行文字:');
      readln(str);
      writeln('您输入的是:',str);
      Writeln(TObject.classname);  //这行代码仅仅表示,可以访问VCL中的System.pas
      if TObject.ClassNameIs('TObject') then
        Writeln('OK');
      readln;
    end.

    7,回调函数与方法指针:

      回调函数是引用C语言中的叫法,在Delphi中,既可以是函数,又可以是过程。

    a,简单的函数指针

     <1>定义语法:

       type

          过程类名=procedure ([参数表列]); //注意,这里procedure后没有过程名

          函数类名=function([参数表列]) : 返回类型;

     end;

       <2>用以上定义的方法类可以定义出一个方法指针变量,并且这个变量可以作为其他方法的参数来使用.

        回调函数的形参实现,以下是阐述部分事例:

           procedure 回调函数名(..., A : 过程类名,...);

           begin

               ...A([参数表列])..  //这里实现了对过程A的使用,使用时不需要考虑A的阐述部分,因为它还没有真正的实体,但是参数表列要跟<1>中定义的一致。

           end;

        <3>主调函数的使用.

           主调函数中,需要定义出一个具有实体的方法,这个方法作为参数使用,替换<2>中的A成为实参。

           这个作为实参出现的方法不需要额外的说明其类型,但是必须要与<1>中的参数表列及返回类型一致。

           eg.

       function 作为实参的方法([参数表列]) : 返回值;

           begin

               ......  //注意,这一部分才是真正的实现了<1>中方法的实体

           end;

           procedure 一个主调的方法;

           begin

               回调函数名(..., 作为实参的方法,...);  //注意,这里的<2>中的A已经被换成了上面的实体方法。 

           end;

        整个回调函数的实现,是分三步的,前两步分别是定义〖方法指针〗及〖回调函数与方法指针的使用关系〗,第三步

    才是真正实现〖方法指针的实体及回调函数的使用〗。

    b,与类和对象相关的方法指针:

        语法定义跟简单方法指针有点象,不过结尾多了 of object:

           过程类名=procedure ([参数表列]) of object;

           函数类名=function([参数表列]) : 返回类型 of object;

        这类的指针往往是指向一个对象方法,甚至类方法(作为类方法指针,前面要加class)来用。

      其指针函数在实际实现时,其实是隐藏了一个self参数的,可以针对对象或者类来进行操作。并且,以of object定义的

    指针,不可以与简单方法指针转换和赋值。

      这也就是我们为什么在窗体中这么写代码会报错:

          button1.onclick=buttonclick; //这里buttonclick虽然单独执行与button1.onclick可能是一样的,但是它仅仅是个本地过程,而onclick是个对象事件。

          我们再来看看事件的标准定义:

          TNotifyEvent = procedure(Sender: TObject) of object;    //这句话其实就是定义了一个方法指针,而OnClick就是这种方法实例。

    c,跨工程接口调用的关键字_cdecl, _stdcall,_fastcall,_cdeclspec

         这节内容实际是与方法指针没有直接关系的,但是一般使用方法指针时又极易涉及到这些关键字,所以放在一起讲了。

         这个下划线是C语言约定的标准,对于不同的语言其使用的方式也不一样。

         cdecl 是C Declare 即C语言标准接口。

         stdcall 是 standard Call ,这个接口标准被Win API 所采用,也是最为通用的调用标准,例如delphi也用它。

         fastcall 在C++Builder当中使用广泛。

         cdeclspc 用的比较少,它的直接意思是一个dll引用方法。

         所有的关键字与其定义接口的关键字必须一致,其实际意义是这些方法及方法参数的压栈方法,细节我在这里不详述。

         下面是一个标准的Delphi外部方法接口的定义:

          {要注意,加了关键字的方法名都是区分大小写的。这里的MTASDK就是外部方法名。}

         function mta001_Exit(): Integer; stdcall; external MTASDK name '_mta001_Exit';

       

     最后再说明一点,假如定义了一个方法指针变量A,那么@A并不代表A的指针,而且是一个标准类型4字节的Pointer指针。

    只有再多加一个@才能代表其方法地址,即:@@A

    8,数组,集合与类型别名的使用。

      a,数组定义:

      var

             数组变量 : array [数组起始下标..终止下标] of 元素类型;

      允许定义复杂点的数组,多维数组及数组的数组。

            数组变量 : array [起始下标1..终止下标1,起始下标2..终止下标2] of 元素类型;  //这是一个二维数组.

            数组变量 : array [起始下标..终止下标] of array [起始下标..终止下标] of 元素类型。 //这是一个数组的数组。

       

           数组允许在定义变量时省略下标,然后在使用时,再用SetLength来设定数组的长度(即开辟内存),这样定义的数组成为动态数组。

    而数组名不带下标时,仅仅等同于一个指针。动态数组的默认起始下标是从0开始的整数。

         数组可以通过Low(数组名)和High(数组名)来返回数组的上下标。

          b,集合定义:

          type

             集合名=set of [元素表列]; //元素表列可以是多个元素用逗号隔开,也可以用上下标加..表示,还可以是枚举类型名。

      TToolButton = (tbAdd, tbDel, tbEdit, tbPost, tbCancel);  //这是一个枚举
      TToolBtnSet = set of TToolButton;                                 //这是一个集合

          这个也可以在定义变量的时候直接定义集合:

          var

               集合变量 : 集合类型=set of [元素表列];

         常用的集合运算有in ,并且可以用+/-[元素表列]来增删集合中的元素。

      注意:集合的类型实例数是有限制的,必须在255以内。例如:集合类型=set of Char; 这样的定义是合法的,因为Char的实例数刚好255,

                  而:集合类型=set of string;   或者:集合类型=set of integer; 这样的定义是会报系统错误的。 所以,一般集合引用枚举类型来定义很常见。

          c,类型别名:

          类型别名 : type 原类型名;

          注意,这样定义的变量,在计算和赋值是是完全兼容的,但是在out型参数使用时,不匹配是会报错的。

          类型别名的定义,有利于代码的优化和变更。

         

    9,多线程的使用

      一个程序就是一个进程,而一个进程中是拥有一个主线程的,我们经常需要设计各种过程的并行执行,这就需要用到线程设计。Delphi中使用线程的方式一般有三种:

    标准的TThread类创建线程对象模式;直接使用WinApi调用模式;伪线程Application.ProcessMessages模式。

          而在线程使用中,最关键的处理核心就在于资源共享,资源使用中,一般是通过资源引用标记来使得其安全释放的,不幸的是,这些资源标记大多时候是需要我们自己来建立和标记的。而且VCL中,可供用户操作的继承于TControl类都不是线程安全的,并且部分非用户界面的VCL类对象也不是线程安全的(例如TList),VCL中有很都针对多线程设计的

    安全类都以Thread前缀来标明(例如TThreadList)。所以,我们尽量不要在进程的excute事件中处理VCL对象,如果非要这么做不可,可以仿照Objective-C中的对象引用计数机制。

          好了,现在就针对线程的三种模式一一说明:

      a,标准的TThread类(这里有我一篇完整的例子分享一个多线程实现[冒泡][选择][二分法]排序的例子)

        TThread类,因为含有一个纯虚方法excute,所以它是一个抽象类,不允许直接建立对象来使用。我们使用它时,必须建立一个非抽象的派生类,再进行使用。

       TThread在Delphi中是可以作为一个单元独立定义的,我们可以从〖新建〗--->〖Delphi Files〗--->〖Thread Object〗来建立一个独立的线程单元。

             TThread类的声名如下:(这里只拣主要属性和方法进行说明)

     TThread = class
      private
         ...
      protected
        procedure DoTerminate; virtual;                                    //线程结束时执行
        procedure Execute; virtual; abstract;                 //线程的主实现过程。执行完毕自动设置Terminated为True,并调用事件OnTerminate
        procedure Synchronize(Method: TThreadMethod);                              //将线程中的一个对象过程,置于程序中的主线程中同步执行,一般被excute中调用。
        property ReturnValue: Integer read FReturnValue write FReturnValue; //可作为函数ThreadProc的返回值
        property Terminated: Boolean read FTerminated;                               //线程结束标记,触发OnTerminate后自动置True
      public
        constructor Create(CreateSuspended: Boolean);                              //构造方法,参数如果为False,则在创建线程后自动执行Excute;如果为True,则挂起
        destructor Destroy; override;
        procedure AfterConstruction; override;
        procedure Resume;                                                                      //唤醒挂起的线程
        procedure Suspend;                                                                     //挂起线程
        procedure Terminate;                                                                   //中断线程
        function WaitFor: LongWord;
        property FatalException: TObject read FFatalException;
        property FreeOnTerminate: Boolean read FFreeOnTerminate write FFreeOnTerminate;  //线程在结束时是否需要自动释放
        property Handle: THandle read FHandle;                                       //线程句柄
    {$IFDEF MSWINDOWS}
        property Priority: TThreadPriority read GetPriority write SetPriority;  //是一个枚举类型,标记着该线程的优先级
    {$ENDIF}
        ...
        property Suspended: Boolean read FSuspended write SetSuspended; //线程是不是挂起状态
    {$IFDEF MSWINDOWS}
        property ThreadID: THandle read FThreadID;                              //线程ID,注意:一个线程是可以有多个句柄的,但是线程ID是唯一的。
    {$ENDIF}
        ...

     //线程的结束事件。默认调用DoTerminate过程,也可以自定义一个事件方法然后用OnTerminate进行指向。
        property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate;  
      end;

     

      假如我们已经派生了一个事件类TMyThread,并实化了Excute方法,那么我们就可以这么产生一个线程

      var

           MyThread1 : TMyThread;

    ....

          MyThread1 := TMyThread.Create(True);               //这个线程是挂起的,如果参数是False,其实MyThread1变量都可以省了。

          MyThread1.OnTerminate := 回调函数;

          MyThread1.FreeOnTerminate  := True;                //其实下面这两句也可以在类TMyThread声明中,用构造函数直接设置死。

          MyThread1.Resume; 

      这样就可以完成运行一个线程了,只不过对于那些非线程安全的对象处理,我们需要在TMyThread的类声明中,用一个对象方法进行处理。

    例如在TMyThread中,有这样一个方法:

      protected

              procedure OperatorVCL;

          ...

          我们可以在Excute实化时这么处理:

          procedure TMyThread.Excute;

          begin

                ...

                Synchronize(OperatorVCL);    //回调处理

          end;

          我们可以这么理解TThread的设计,当一个程序中出现一个任何一个附属线程的时候,程序会自动建立一个0X0的隐藏窗口,用来接收线程的Synchronize方法。并且,所有的附加线程,都是共用这个隐藏窗口的,并且在传递Synchronize方法时,将其对应的线程Self作为一个IParam传给主线程。

      b,跳过Thread类,直接用Win Api模式来实现一个进程:

          //定义一个线程句柄

          var

              hThread: THandle;     

          //定义一个线程的主方法

         function MainThreadMethod( AParam : integer ) : boolean; stdcall; //这里是过程,函数都可以,参数返回值也可以随便定义,但是stdcall关键字不能少。

         begin

            ...

         end;

         //直接创建线程,并唤醒或挂起

         var
             ID: DWORD;

         ...

         hThread := CreateThread(nil, 0, @MainThreadMethod, nil, CREATE_SUSPENDED, ID);

         ResumeThread(hThread);

         SuspendThread(hThread);

         ...

         这个事例中用CreateThread,ResumeThread,SuspendThread三个方法直接用API函数完成了对一个线程的使用。

         当然,还有一系列的线程方法,例如:TerminateThread,ExitThread,GetCurrentThread: THandle;GetCurrentThreadId: DWORD; 等等。

         其实,用TThread类实现多线程时,也调用CreateThread和ExitThread方法,不过Delphi重新定义了一个线程方法:BeginThread(非对象或类方法哦),

    这个方法将CreateThread系列方法打包简化了,并且在Thread的构造函数中默认调用了BeginThread方法。

         注意:API系列的Thread方法是在Windows单元定义的;BeginThread方法是在System单元定义的;而TThread的类定义,是在Classes单元定义的。

         c,伪线程操作Application.ProcessMessages

         这个方法一般是用在循环当中,其作用是防止在一个事件中执行循环的时候,把主线程完全占有,这时候窗体操作UI处于死锁状态,即不能停止,也不能点击或拖动。

      而增加了Application.ProcessMessages,就可以让程序执行到这里响应一个Windows的标准消息,这种〖分步〗执行的效果也跟多线程类似。

          我们可以在系统响应Application.ProcessMessages的消息的时候,配合Sleep语句,并改变一个控件内容,从而达到控制循环条件的效果。

      但是有一点一定要注意:Application.ProcessMessages 一定不能在OnTimer事件中使用,否则会造成消息迭代,主线程阻塞。虽然OnTimer的机制跟线程非常

    相似,但是其实它是基于主线程实现的,任务在这里阻塞一样会影响到主线程的运行。

         

    不求功能强大,但求小巧融洽
  • 相关阅读:
    【Java】组合 继承 代理
    《Thinking In Java》笔记之十三章 字符串
    常用Dos命令
    Thinking in Java异常笔记与习题
    php去重 逗号分隔的字符串
    php 连接本地数据库
    vue重载子组件
    小程序更改checked样式
    JavaScript中两个数组的拼接
    FROM_UNIXTIME()时间戳转换函数
  • 原文地址:https://www.cnblogs.com/Murphieston/p/5577836.html
Copyright © 2020-2023  润新知