【问题标题】:Is it possible to get components' method value (means name)?是否可以获得组件的方法值(即名称)?
【发布时间】:2014-06-06 16:37:21
【问题描述】:

我需要获取一个组件的名称 (TButton),它在设计时分配并在 Object Inspector 中看到(例如 Button1Click at Button1.OnClick 事件选项卡上的事件)。

我现在使用 TypInfo 单元通过PPropInfo 获取方法的信息,我得到OnClickTNotifyEvent 字符串作为值,但我没有得到Button1Click 作为字符串值。

我怎样才能得到它?

【问题讨论】:

    标签: delphi


    【解决方案1】:
    string := MethodName(GetMethodProp(Button1, 'OnClick').Code);
    

    请注意,该方法需要“发布”。

    【讨论】:

    • 这里也假设方法是由表单实现的。 +1
    • GetMethodProp() 返回一个TMethodTMethod.Data 字段指向传递给方法的Self 参数的对象。在 99% 的情况下,您可以将 TMethod.Data 类型转换为 TObject,然后使用该对象以 TMethod.Code 作为地址调用 TObject.MethodName(),因为该地址将属于该对象的类。这应该适用于所有情况,即使事件是在运行时在代码中分配的。
    • @RemyLebeau 如果在这种情况下没有分配任何方法或 AV 会正常运行吗?顺便说一句 - 谢谢!我明天上班试试;)
    • 已经检查过了!一切运行良好!如果没有分配任何内容,则没有 AV - 只是 '' (空字符串)。
    • @notricky:在你的源代码中它必须是published,它不能在运行时更改为published。有关详细信息,请参阅the documentation
    【解决方案2】:

    如果属性和赋值方法都是published,你可以这样:

    uses
      TypInfo;
    
    function GetEventHandlerName(Obj: TObject; const EventName: String): String;
    var
      m: TMethod;
    begin
      m := GetMethodProp(Obj, EventName);
      if (m.Data <> nil) and (m.Code <> nil) then
        Result := TObject(m.Data).MethodName(m.Code)
      else
        Result := '';
    end;
    

    s := GetEventHandlerName(Button1, 'OnClick');
    

    TypInfo 单元(GetMethodProp() 的来源)仅支持 published 属性。

    您必须指定拥有方法地址的对象,因为TObject.MethodName() 会迭代对象的 VMT。并且方法必须是 published,因为 TObject.MethodName()(它的存在是为了促进 DFM 流式传输)迭代 VMT 的一部分,该部分仅填充有 published 方法的地址。

    如果您使用的是 Delphi 2010 或更高版本,则可以改用 Extended RTTI,它没有 published 的限制:

    uses
      Rtti;
    
    function GetEventHandlerName(Obj: TObject; const EventName: String): String;
    type
      PMethod = ^TMethod;
    var
      ctx: TRttiContext;
      v: TValue;
      _type: TRttiType;
      m: TMethod;
      method: TRttiMethod;
      s: string;
    begin
      Result := '';
      ctx := TRttiContext.Create;
      v := ctx.GetType(Obj.ClassType).GetProperty(EventName).GetValue(Obj);
      if (v.Kind = tkMethod) and (not v.IsEmpty) then
      begin
        // v.AsType<TMethod>() raises an EInvalidCast exception
        // and v.AsType<TNotifyEvent>() is not generic enough
        // to handle any kind of event. Basically, the Generic
        // parameter of AsType<T> must match the actual type
        // that the event is declared as.  You can use
        // TValue.GetReferenceToRawData() to get a pointer to
        // the underlying TMethod data...
        m := PMethod(v.GetReferenceToRawData())^;
        _type := ctx.GetType(TObject(m.Data).ClassType);
        for method in _type.GetMethods do
        begin
          if method.CodeAddress = m.Code then
          begin
            Result := method.Name;
            Exit;
          end;
        end;
      end;
    

    s := GetEventHandlerName(Button1, 'OnClick');
    

    【讨论】:

    • 转换为 TObject 是冒昧的。 Data 指针可以引用一个类而不是一个实例。或者实际上是一个记录或一个 TP 对象。
    • 知道有多好。 @DavidHeffernan 解决这个问题的方法是什么?
    • @notricky 你真的有问题吗?您不会从 TP 对象中获得任何 RTTI,但谁在乎它们呢?至于记录或类类型,不确定如何识别。知道你为什么问这个问题会很有趣。
    • @DavidHeffernan 很公平。我正在写一个SmallClass,我在其中传递自身和外部组件。 Component 有它的方法,我需要找到并重新分配这些方法,这些方法在 self 的实例中按名称命名。当Componentself 都具有相同的方法名称时,它应该会发生:Component.OnClick - 'ComponentClick'self.ComponentClick。所以我不仅需要从Component 中的已发布属性中获取信息,还需要从self 中获取信息。
    • 我真的不明白为什么你需要使用自省,但无论如何我认为除了方法指针的原始数据之外不需要知道任何东西。我认为这里的两个答案,虽然很好地回答了你的问题,但比你需要的更复杂。换句话说,我认为你问错了问题。
    猜你喜欢
    • 1970-01-01
    • 2020-07-17
    • 2017-02-27
    • 2016-07-19
    • 1970-01-01
    • 1970-01-01
    • 2017-04-30
    • 1970-01-01
    • 2018-04-16
    相关资源
    最近更新 更多