Delphi 通过通用接口的异步事件

Delphi 通过通用接口的异步事件,delphi,asynchronous,delphi-2007,tcomport,Delphi,Asynchronous,Delphi 2007,Tcomport,我必须将多个测量设备连接到我的应用程序(即卡尺、体重秤等),而不是绑定到特定的品牌或型号,因此在客户端,我使用带有通用方法的接口(QueryValue)。设备通过COM端口连接,并以异步方式访问: 请求一个值(=发送一个特定的字符序列 COM端口) 等待答复 在“业务”方面,我的组件在内部使用TComPort,数据接收事件是TComPort.OnRxChar。我想知道如何通过接口触发此事件?以下是我迄今为止所做的工作: IDevice = interface procedure QueryV

我必须将多个测量设备连接到我的应用程序(即卡尺、体重秤等),而不是绑定到特定的品牌或型号,因此在客户端,我使用带有通用方法的接口(
QueryValue
)。设备通过COM端口连接,并以异步方式访问:

  • 请求一个值(=发送一个特定的字符序列 COM端口)
  • 等待答复
  • 在“业务”方面,我的组件在内部使用TComPort,数据接收事件是
    TComPort.OnRxChar
    。我想知道如何通过接口触发此事件?以下是我迄今为止所做的工作:

    IDevice = interface
      procedure QueryValue;
      function GetValue: Double;
    end;
    
    TDevice = class(TInterfacedObject, IDevice)
    private
      FComPort: TComPort;
      FValue: Double;
    protected
      procedure ComPortRxChar;
    public
      constructor Create;
      procedure QueryValue;
      function GetValue: Double;
    end;
    
    constructor TDevice.Create;
    begin
      FComPort := TComPort.Create;
      FComPort.OnRxChar := ComPortRxChar;
    end;
    
    // COM port receiving data
    procedure TDevice.ComPortRxChar;
    begin
      FValue := ...
    end;
    
    procedure TDevice.GetValue;
    begin
      Result := FValue;
    end;
    

    但我需要一个事件来知道何时在客户端调用
    GetValue
    。执行这种数据流的通常方法是什么?

    您可以向接口添加事件属性

    IDevice = interface
      function GetValue: Double;
      procedure SetMyEvent(const Value: TNotifyEvent);
      function GetMyEvent: TNotifyEvent;
      property MyEvent: TNotifyEvent read GetMyEvent write SetMyEvent;
    end;
    
    并在TDevice类中实现

    TDevice = class(TInterfacedObject, IDevice)
    private
      FMyEvent: TNotifyEvent;
      procedure SetMyEvent(const Value: TNotifyEvent);
      function GetMyEvent: TNotifyEvent;
    public
      function GetValue: Double;
      procedure EmulChar;
    end;
    
    然后像通常一样,在
    ComPortRxChar
    的末尾调用
    FMyEvent
    处理程序(如果已分配)

     Tform1...
      procedure EventHandler(Sender: TObject);
    
    procedure TForm1.EventHandler(Sender: TObject);
    var
      d: Integer;
      i: IDevice;
    begin
      i := TDevice(Sender) as IDevice;
      d := Round(i.GetValue);
      ShowMessage(Format('The answer is %d...', [d]));
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      id: IDevice;
    begin
      id:= TDevice.Create;
      id.MyEvent := EventHandler;
      (id as TDevice).EmulChar; //emulate rxchar arrival
    end;
    
    procedure TDevice.EmulChar;
    begin
      if Assigned(FMyEvent) then
        FMyEvent(Self);
    end;
    
    function TDevice.GetMyEvent: TNotifyEvent;
    begin
      Result := FMyEvent;
    end;
    
    function TDevice.GetValue: Double;
    begin
      Result := 42;
    end;
    
    procedure TDevice.SetMyEvent(const Value: TNotifyEvent);
    begin
      FMyEvent := Value;
    end;
    

    你能冲洗一下吗?在
    t设备中
    如何将
    FMyEvent
    连接到
    Get/SetMyEvent
    ?如果在
    TDevice.ComPortRxChar
    方法中,我写
    如果赋值(FMyEvent),那么FMyEvent(Self)(就像我以前做的-我的意思是没有接口),什么时候
    Get/SetMyEvent
    是隐含的?谢谢!我就是这样理解的;)这是一个非常好的解决方案,但我仍然看到了一个缺点:由于我只在客户端使用接口,而不使用实现(这是主要目标),
    EventHandler
    中是否有办法将
    发送方
    转换为
    IDevice
    ?否则,我应该将IDevice变量(您在
    按钮1click
    中的“id”)声明为类变量。我的示例将Sender转换为IDevice。我不明白为什么IDevice应该是一个类变量……你的IDevice的生命周期是什么?在
    EventHandler
    中,
    TDevice(Sender)
    TDevice
    类型紧密耦合,但它不应该。与
    id:=TDevice.Create相同
    按钮1中单击
    方法,但举例来说,没问题,我使用了一个类型解析器,它完成所有依赖项注入工作。如果我无法将
    发送方
    强制转换为预期类型,则类变量将在所有类方法中使用相同的变量。是否希望EventHandler参数具有接口类型?我认为可以使用IDevice参数声明处理程序过程类型,并使用它代替TNotifyEvent。如果您的IDevice是单例的,您可以完全避免使用Sender。