代码看起来可能有点烦,不过都是些简单的code,请高手指点迷津!
线程同步类中的published方法时,Receive为inaccessible value,怎样解决?
分数可以再加.部分代码如下:...type
  TForm1 = class(TForm)
  ...
  end;  //定义函数指针对象类型
  TFunc = function: Integer of Object;  //定义接收线程
  TReceiveThread = class(TThread)
  private
    FFunc: TFunc;
  protected
    procedure SynKernel;
    procedure Execute; override;
  end;
 
  //定义核心类
  TKernel = class
  public
    Receive: TList; //接收信息缓冲
    function IsCreate: Integer;
  published
    destructor Destroy; override;
    function SynReceive: Integer; //线程同步核心类方法
  end;var
  Form1: TForm1;
  Kernel: TKernel;
  Re: TReceiveThread;implementation{$R *.dfm}{====================TReceiveThread====================}
procedure TReceiveThread.SynKernel;
begin
  TMethod(FFunc).Code := TKernel.MethodAddress('SynReceive');
  if Assigned(TMethod(FFunc).Code) then
    if FFunc=0 then ShowMessage('not assign')
    else ShowMessage('assign');
end;procedure TReceiveThread.Execute;
begin
  FreeOnTerminate := True;
//  while not Terminated do
    Synchronize(SynKernel);
end;{====================TKernel====================}
function TKernel.SynReceive: Integer;
begin
  Result := 0;
  //线程同步类中的published方法时,Receive为inaccessible value,怎样解决?
  if Receive=nil then Exit;
  //同样,这句代码也不行
//  if not Assigned(Receive) then Exit;
  Result := 1;
end;constructor TKernel.Create;
begin
  inherited;
  Receive := TList.Create;
end;destructor TKernel.Destroy;
begin
  Receive.Free;
  inherited;
end;function TKernel.IsCreate: Integer;
begin
  if Receive=nil then
    Result := 0
  else Result := 1;
end;
{====================TForm1====================}
procedure TForm1.FormCreate(Sender: TObject);
begin
  Kernel := TKernel.Create;  Re := TReceiveThread.Create(False);
end;procedure TForm1.FormDestroy(Sender: TObject);
begin
  Kernel.Destroy;
end;procedure TForm1.Button1Click(Sender: TObject);
begin
  //这里的调用一切正常
  case Kernel.IsCreate of
    0: Caption := 'not assign';
    1: Caption := 'assign, no data';
  end;  
end;

解决方案 »

  1.   

    TFunc是一个方法类型,注意一个方法类型和一个过程类型相比多出了一个指针,该指针指向方法所属对象。对应到TMethod,就是它的Data字段。而你只是指定了Code而没有指定Data。因此:
        if FFunc = 0 
    进行调用时,FFunc的第二个指针(指向所属对象)为nil,那么就造成:
        if Receive = nil 引用失败,因为它相当于:if Self.Receive = nil,而此时Self和Data是同一个指针而不是Kernel,因为你是通过FFunc而不是SynReceive调用。解决此问题有两个办法:
    1、治标法:将
    if Receive = nil 改为 
    if Kernel.Receive = nil
    2、治本法:增加一句:
    TMethod(FFunc).Data := Kernel;————————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    ————————————————————————————————————
      

  2.   

    Delphi中只有一种过程类型,而不论原型是Function还是Procedure当过程类型的原型是类的一个方法的时候,方法类型变量为一对指针,第一个指向方法入口地址,第二个指向方法所属类的实体!所以可以利用TMethod直接进行类型转换来获取对应地址!
      

  3.   

    TO: lxpbuaa(桂枝香在故国晚秋)
    谢谢热心的回复,实在惭愧,你的高论我还不能完全理解,class方法间的调用不指定另一class的实例handle,直接访问这个class的变量是行不通的。看来也只有引用class的实例才能行的通!
    不知lxpbuaa兄是否还有更好的解决方案?如果能全部封装成一个Object当然更好!TO: FrameSniper(§绕瀑游龙§)
    多谢...
      

  4.   

    amiao(射手座男孩) (▲▲▲▲):
    如果你不明白我的意思,那么直接将:
    if Receive = nil then Exit;
    改为:
    if Kernel.Receive = nil then Exit; 或者在:
    TMethod(FFunc).Code := TKernel.MethodAddress('SynReceive');
    后面加一句:
    TMethod(FFunc).Data := Kernel;—————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    —————————————————————————————————
      

  5.   

    TO: lxpbuaa
    这么晚了还在,PFPF...我的意思是说不用Kernel的解决方案,因为这样封装的不好
      

  6.   

    如果这样,还不如将SynReceive写给外面,调用也不用这么麻烦
    定义TFunc为function: Integer,然后直接给FFunc赋值为SynReceive
      

  7.   

    amiao(射手座男孩) (▲▲▲▲):
    我现在在下棋,一会回复你……—————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    —————————————————————————————————
      

  8.   

    var
      Form1: TForm1;
      Kernel: TKernel;
      Re: TReceiveThread;Form1是主窗体,与用户交互
    Kernel是从谁那里引申过来,而又其它类无关时,则Kernel应为那个类的private变量,否则你定义为全局变量就是一个错误的决定。
    Re如Kernelprocedure TReceiveThread.SynKernel;
    begin
      TMethod(FFunc).Code := TKernel.MethodAddress('SynReceive');
      ...
    end;FFunc是TReceiveThread的private,而SynReceive为TKernel成员函数,通赤MethodAddress来得到函数指针,如果SynReceive没有访问TKernel的成员变量(如访问Receive: TList),则好像没什么事,如果有,则会产生AV。概念不对。function TKernel.SynReceive: Integer;
    begin
      Result := 0;
      //线程同步类中的published方法时,Receive为inaccessible value,怎样解决?
      if Receive=nil then Exit;
      //同样,这句代码也不行
    //  if not Assigned(Receive) then Exit;
      Result := 1;
    end;
    你没有搞清楚成员变量在一个类实例还没产生,是没有初始化的,当然会产生AV。
    因为上面那句:
    TMethod(FFunc).Code := TKernel.MethodAddress('SynReceive');
    就不对
    TMethod(FFunc).Code := Kernel.MethodAddress('SynReceive');
    or 
    FFunc := Kernel.SynReceive;类与类的封装很重要的一个体现就是不会使用一个莫名的全局变量,你要搞清的是每个类它的依赖关系,还有一个类实例与一个类是不一样的。
      

  9.   

    这么作吧,将线程类再封装一下:
    TReceiveThread = class(TThread)
      private
        FFunc: TFunc;
        KernelObj: TObject;
        KernelMethodName: ShortString;
      protected
        procedure SynKernel;
        procedure Execute; override;
      public
        constructor Create(CreateSuspended: Boolean; KernelObj: TObject; KernelMethodName: ShortString); overload; //重载一个构造器
      end;如下实现:
    constructor TReceiveThread.Create(CreateSuspended: Boolean; KernelObj: TObject; KernelMethodName: ShortString);
    begin
      inherited Create(CreateSuspended);
      Self.KernelObj := KernelObj;
      Self.KernelMethodName := KernelMethodName;
    end;
    procedure TReceiveThread.SynKernel;
    begin
      if (KernelObj <> nil) and (KernelMethodName <> '') then
      with TMethod(FFunc) do
      begin
         Code := KernelObj.MethodAddress(KernelMethodName);
         Data := KernelObj;
         if Assigned(Code) then
           if FFunc = 0 then ShowMessage('not assign')
           else ShowMessage('assign');
      end;
    end;调用:
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Kernel := TKernel.Create;
      Re := TReceiveThread.Create(False, Kernel, 'SynReceive');
    end;这次行了吧?已经完全封装。
    —————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    —————————————————————————————————
      

  10.   

    copy_paste(木石三) (★★)兄:
    TKernel.MethodAddress('SynReceive');是正确的,因为方法的地址只是和类有关而与实例无关,因此TObject.MethodAddress被定义了一个类方法,不一定只有实例采用调用它。问题的关键是普通方法FFunc需要由实例调用,而它的代码里没有指定其Data(即实例),因此调用失败。至于其他的一些问题,如定义了一些不必要的全局变量,倒在其次。—————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    —————————————————————————————————
      

  11.   

    谢谢楼上lxpbuaa兄,OOP我还是门外汉:(顺便问句,怎么加分?
      

  12.   

    如果不能解决TMethod(FFunc).Data = nil 的问题,即使使用:
    TMethod(FFunc).Code := Kernel.MethodAddress('SynReceive');
    也是无济于事。
    但如果直接使用:
    FFunc := Kernel.SynReceive;
    那么又没有体现类的封装性,因此像上面我说的那样重载线程类的构造器,应该是个比较好的解决方案。—————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    —————————————————————————————————
      

  13.   

    TMethod只要传来Code值,即可以运行,不一定需要Data值,
    如下:type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        FInt2: Integer;
      published
        function GetInt1: Integer;
        function GetInt2: Integer;
      end;  TFunc = function: Integer of object;  TChild = class
      private
        FFunc: TFunc;
      public
        constructor Create;
      end;implementation{ TForm1 }function TForm1.GetInt1: Integer;
    begin
      Result := 1;
    end;function TForm1.GetInt2: Integer;
    begin
      Result := FInt2;
    end;{ TChild }constructor TChild.Create;
    begin
      inherited Create;
      TMethod(FFunc).Code := TForm1.MethodAddress('GetInt1');
      if Assigned(FFunc) then
        ShowMessage(IntToStr(FFunc));
      { 这是类实例的GetInt2方法指针 }
      TMethod(FFunc).Code := Form1.MethodAddress('GetInt2');
      if Assigned(FFunc) then
        ShowMessage(IntToStr(FFunc));  { 这是类的GetInt2方法指针 }
      TMethod(FFunc).Code := TForm1.MethodAddress('GetInt2');
      if Assigned(FFunc) then
        ShowMessage(IntToStr(FFunc));
    end;procedure TForm1.Button1Click(Sender: TObject);
    begin
      TChild.Create.Free;
    end;
      

  14.   

    copy_paste(木石三) (★★)兄:
    不知道你发现没有,通过FFun调用GetInt2时就出错了。为什么调用GetInt1正确而GetInt2出错呢,是因为GetInt1中只是对常量1操作而GetInt2中操作了变量FInt2,取得FInt2就必须通过FFun.Data,而此时它为nil,也就是说相当于使用:
    nil.FInt2
    自然就错了。—————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    —————————————————————————————————
      

  15.   

    copy_paste(木石三) (★★)兄:
    我是从这个帖子才开始明白的,呵呵:》—————————————————————————————————
    宠辱不惊,看庭前花开花落,去留无意;毁誉由人,望天上云卷云舒,聚散任风。
    —————————————————————————————————
      

  16.   

    //这样行不
    type
      TFunc = function: Integer of Object;
      TReceiveThread = class(TThread)
      private
        FFunc: TFunc;
      protected
        procedure SynKernel;
        procedure Execute; override;
      end;  TKernel = class
        private
        Receive: TList;
        function IsCreate: Integer;
       public
        constructor Create;
        destructor Destroy; override;
        function SynReceive: Integer;
      end;var
      Kernel: TKernel;
      Re: TReceiveThread;procedure TReceiveThread.SynKernel;
    begin
      FFunc:= Kernel.SynReceive;
      if Assigned(FFunc) then
        if FFunc=0 then
           ShowMessage('not assign')
        else
          ShowMessage('assign');
    end;procedure TReceiveThread.Execute;
    begin
      FreeOnTerminate := True;
      Synchronize(SynKernel);
    end;
    function TKernel.SynReceive: Integer;
    begin
      Result := 0;
      if Receive=nil then
        Exit;
      Result := 1;
    end;constructor TKernel.Create;
    begin
      inherited;
      Receive := TList.Create;
    end;destructor TKernel.Destroy;
    begin
      Receive.Free;
      inherited;
    end;function TKernel.IsCreate: Integer;
    begin
      if Receive=nil then
        Result := 0
      else Result := 1;
    end;