【问题标题】:Passing object in reference / one place to style objects在参考/一个地方传递对象以设置对象样式
【发布时间】:2011-07-23 04:31:09
【问题描述】:

我有一个相当大的应用程序,目前正在设计中。 为了节省我更改 IDE/Object Inspector 中的所有按钮的时间,我计划只为主要对象执行一些功能,例如

procedure StyleButton(AButton : TButton)
begin
    AButton.Color := clGreen;
    AButton.Font.Style = [fsBold];
end;

etc 等,然后根据需要将其添加到 onCreates 表单中

StyleButton(Button1); whatever etc

像这样在参数中传递对象没有问题。它只是引用第一个对象对吗?

它运行良好,我想不出任何问题,但因为这是一个拥有数千名用户的大型应用程序,我只想确保不会出现问题/内存泄漏/资源消耗问题。

也将使用 TAdvStringGrid 和 TEdit/TMemo 组件做类似的事情。

然后只允许 1 个位置更改这些设置。

或者有人有更好的主意?

【问题讨论】:

标签: delphi object pass-by-reference


【解决方案1】:

这是一个绝妙的主意。该函数将修改您传递给它的任何对象。

您没有通过引用传递。你是按价值传递的。您传递的值 是一个 引用。 “通过引用传递”意味着您将使用 varout 关键字,这适合这种情况。

【讨论】:

    【解决方案2】:

    正如其他回答者已经说过的那样,您的想法很好。只是想提出一个比大卫更进一步的解决方案,以及您可能需要考虑的一些内容,以避免添加许多语句,例如:

    StyleButton(Button1);
    StyleButton(Button2);
    

    到您想要设置样式的每个控件的每个表单;

    我的建议是为每个表单的 OnShow 事件添加一个方法调用:

    procedure TForm1.FormShow(Sender: TObject);
    begin
      TStyler.StyleForm(Self);
    end;
    

    TStyler 可以在一个单独的单元中实现,如下所示:

    interface
    
    type
      TStyler = class;
      TStylerClass = class of TStyler;
    
      TStyler = class(TObject)
      public
        class procedure StyleForm(const aForm: TCustomForm);
        class procedure StyleControl(const aControl: TControl); virtual;
        class function GetStyler(const aControl: TControl): TStylerClass;
      end;
    
    implementation
    
    uses
      Contnrs;
    
    type
      TButtonStyler = class(TStyler)
      public
        class procedure StyleControl(const aControl: TControl); override;
      end;
    
      TEditStyler = class(TStyler)
      public
        class procedure StyleControl(const aControl: TControl); override;
      end;
    
      TLabelStyler = class(TStyler)
      public
        class procedure StyleControl(const aControl: TControl); override;
      end;
    
    var
      _Controls: TClassList;
      _Stylers: TClassList;
    
    { TStyler }
    
    class function TStyler.GetStyler(const aControl: TControl): TStylerClass;
    var
      idx: Integer;
    begin
      Result := TStyler;
      idx := _Controls.IndexOf(aControl.ClassType);
      if idx > -1 then
        Result := TStylerClass(_Stylers[idx]);
    end;
    
    class procedure TStyler.StyleForm(const aForm: TCustomForm);
    
      procedure _StyleControl(const aControl: TControl);
      var
        i: Integer;
        StylerClass: TStylerClass;
      begin
        StylerClass := TStyler.GetStyler(aControl);
        StylerClass.StyleControl(aControl);
        if (aControl is TWinControl) then
          for i := 0 to TWinControl(aControl).ControlCount - 1 do
            _StyleControl(TWinControl(aControl).Controls[i]);
      end;
    
    var
      i: Integer;
    begin
      _StyleControl(aForm);
    end;
    
    class procedure TStyler.StyleControl(const aControl: TControl);
    begin
    // Do nothing. This is a catch all for all controls that do not need specific styling.
    end;
    
    { TButtonStyler }
    
    class procedure TButtonStyler.StyleControl(const aControl: TControl);
    begin
      inherited;
      if aControl is TButton then
      begin
        TButton(aControl).Font.Color := clRed;
        TButton(aControl).Font.Style := [fsBold];
      end;
    end;
    
    { TEditStyler }
    
    class procedure TEditStyler.StyleControl(const aControl: TControl);
    begin
      inherited;
      if aControl is TEdit then
      begin
        TEdit(aControl).Color := clGreen;
      end;
    end;
    
    { TLabelStyler }
    
    class procedure TLabelStyler.StyleControl(const aControl: TControl);
    begin
      inherited;
      if aControl is TLabel then
      begin
        TLabel(aControl).Font.Color := clPurple;
        TLabel(aControl).Font.Style := [fsItalic];
      end;
    end;
    
    initialization
      _Controls := TClassList.Create;
      _Stylers := TClassList.Create;
    
      _Controls.Add(TButton);
      _Stylers.Add(TButtonStyler);
    
      _Controls.Add(TEdit);
      _Stylers.Add(TEditStyler);
    
      _Controls.Add(TLabel);
      _Stylers.Add(TLabelStyler);
    
    finalization
      FreeAndNiL(_Controls);
      FreeAndNiL(_Stylers);
    end.
    

    这个解决方案基本上采用了多态性和一个将控制类链接到样式器类的注册表。它还使用类过程和函数来避免实例化任何东西。

    请注意,在此示例中,注册表实现为两个需要手动保持同步的列表,因为代码假定在索引 X 处查找类将在另一个列表中的相同索引处找到样式器。这当然可以改进很多,但在这里足以展示这个概念。

    【讨论】:

    • @David:是的,如果我有一个多小时的时间来完成这个,我可能还会添加递归... ;) 应该很容易添加,只有一种方法改变,我相信 OP 可以做到这一点,但如果你再给我半个小时,我也会......虽然没有时髦的枚举器,我会把它作为练习留给读者。 :-)
    • 确实很好。我去散步并且正在考虑一些事情,我将在此处添加它作为答案,但有人认为这可能是一个问题,如果您希望某些按钮以一种方式设置样式。例如,确定按钮可能需要绿色,而取消按钮可能需要灰色。
    • @David:无论如何我都想为自己做这件事,一个小问题是你需要一个 TWinControl 而不是 TControl 来吸引孩子和孙子孙女。 (尽管我不想要任何!)顺便感谢您的编辑,那是我的荷兰人闪耀...
    • @Wizzard:谢谢。 Button 的事情可以通过使用按钮的“取消”属性来解决。当然,总是有 Tag 属性来区分同一类的控件(如果您还没有将其用于其他用途)。
    • @Marjan 是的,TWinControl/TControl,你必须完成那个。如果没有 TStyler.StyleForm 中的循环并调用 _StyleControl(aForm),你会更好。一个就够了,为什么还要写两个循环?
    【解决方案3】:

    不,没有问题(在您的特定情况下)将对象作为参数传递

    procedure StyleButton(AButton : TButton)
    

    当你这样做时,你是在传递一个地址内存(引用)并设置被引用对象的一些属性,所以没有问题。

    【讨论】:

      【解决方案4】:

      为了补充 Rob 和 RRUZ 已经说过的内容,您可以考虑使用开放数组参数的额外帮助器:

      procedure StyleButtons(const Buttons: array of TButton);
      var
        i: Integer;
      begin
        for i := low(Buttons) to high(Buttons) do
          StyleButton(Buttons[i]);
      end;
      

      你可以这样称呼它:

      StyleButtons([btnOK, btnCancel, btnRelease64bitDelphi]);
      

      在我看来,这在呼叫站点比:

      StyleButton(btnOK);
      StyleButton(btnCancel);
      StyleButton(btnRelease64bitDelphi);
      

      请注意,我将开放数组作为 const 参数传递,因为这样在处理数组时效率更高。因为数组的每个元素本身都是对按钮的引用,所以您可以修改实际的按钮。 const 只是意味着您不能更改引用。

      【讨论】:

      • 感谢有关将 const 用于数组的提示,我正在考虑使用这样的方法来提供帮助,并且您已经证明它很有帮助。再次感谢。
      猜你喜欢
      • 2013-05-28
      • 1970-01-01
      • 1970-01-01
      • 2020-08-22
      • 1970-01-01
      • 2013-10-22
      • 1970-01-01
      • 2012-01-23
      • 1970-01-01
      相关资源
      最近更新 更多