【问题标题】:Delphi 2009: Component Object property default valueDelphi 2009:组件对象属性默认值
【发布时间】:2016-03-17 18:11:30
【问题描述】:

如何设置组件对象属性值的默认值?

组件类代码:

unit CustomClass;

interface

uses
    Classes;

type
  TCustomClass = class;

  TConfiguration = class;

  TCustomClass = class (TComponent)
  private
    FConfiguration: TConfiguration;
    procedure SetConfiguration(const Value: TConfiguration);
  published
    property Configuration: TConfiguration read FConfiguration write SetConfiguration;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  end;

  TConfiguration = class (TPersistent)
  private
    FDelay: Integer;
    FSize: Integer;
    procedure SetDelay(const Value: Integer);
    procedure SetSize(const Value: Integer);
  published
    property Delay: Integer read FDelay write SetDelay;
    property Size: Integer read FSize write SetSize;
  public
    procedure Assign(Source: TPersistent); override;
  end;

implementation

{ TCustomClass }

constructor TCustomClass.Create(AOwner: TComponent);
begin
  inherited;
  Configuration.FileName:= 'FileName';
  Configuration.Size := 10;
end;

destructor TCustomClass.Destroy;
begin
  Configuration.Free;
  inherited;
end;

procedure TCustomClass.SetConfiguration(const Value: TConfiguration);
begin
  FConfiguration.Assign(Value);
end;

{ TConfiguration }

procedure TConfiguration.Assign(Source: TPersistent);
begin
  inherited;
  Delay := (Source as TConfiguration).Delay;
  Size := (Source as TConfiguration).Size;
end;

procedure TConfiguration.SetDelay(const Value: Integer);
begin
  FDelay := Value;
end;

procedure TConfiguration.SetSize(const Value: Integer);
begin
  FSize := Value;
end;

end.

在我的 IDE 中,它将显示为对象属性已修改(粗体蓝色)。

我想在 TConfiguration 类属性上创建默认和存储函数,如下所示:

TConfiguration

interface

private
  function FileNameStored: Boolean;
published
  property FileName: string read FFileName write SetFileName stored FileNameStored;
  property Size: Integer read FSize write SetSize default 10;

implementation

function TConfiguration.FileNameStored: Boolean;
begin
  Result := FileName <> 'FileName';
end;

它将 TConfiguration 的属性着色为普通蓝色,但不是 TCustomClass 的 Configuration 属性,而且我想设置其默认值也不存在,它位于 TCustomClass 上,因为 TConfiguration 可能用于其他组件。

然后,我也想:

TCustomClass

interface

private
  function ConfigurationStored: Boolean;
published
  property Configuration: TConfiguration read FConfiguration write SetConfiguration stored ConfigurationStored;

implementation

function TCustomClass.ConfigurationStored: Boolean;
begin
  Result := Configuration.FileName <> 'FileName' and
    Configuration.Size <> 10;
end;

但是,这只会将 TCustomClass 配置属性颜色设置为普通蓝色,而不是其属性。

回答

正如@RemyLebeau 所指出的(在第一个和最佳答案中),代码中存在错误。 在那个组件和那种情况下,我决定不对属性设置任何默认值。

【问题讨论】:

  • 您是否打算将配置对象拖放到表单上并将自定义类拖放到表单上,然后将它们连接起来?或者您是否希望配置对象成为属于自定义类的属性,而不必手动创建和分配?
  • 那么你真的忽略了一些明显的事情,正如 Remy 指出的那样。他表明您需要显式创建(在您的类构造函数中)和释放(在您的类析构函数中)属于您的对象(是不可分割的部分)的所有 TObject 类型。你有正确的基类,所以这很好。您可能希望将 OnChange:TNotificationEvent 添加到您的配置对象。所以它可以调用主类并让它在有人写入配置属性时做一些事情。
  • @WarrenP 是的。我修复了它,但正如@NGLN 指出的那样,在问题上纠正它是没有意义的,因为它使答案变得不必要。 OnChange: TNotificationEvent 确实是一个很好的建议,我喜欢它,因为如果它们是类私有变量,我可以更改属性的 stored 值。谢谢。

标签: class delphi properties components delphi-2009


【解决方案1】:

您的代码中有几个错误。

  1. 您的 TCustomClass 构造函数没有创建 TConfiguration 对象。您需要添加:

    constructor TCustomClass.Create(AOwner: TComponent);
    begin
      inherited;
      FConfiguration := TConfiguration.Create; // <-- add this
      FConfiguration.FileName := 'FileName';
      FConfiguration.Size := 10;
    end;
    

    话虽如此,FileNameSize 属性的赋值可能应该移动到 TConfiguration 构造函数而不是 TCustomClass 构造函数。

  2. 不能使用用户定义的默认值定义String 属性,默认值始终为空字符串。因此,您的 FileName 属性在创建组件时将始终显示为已修改,因为您的构造函数为其分配了非默认值。您的 stored 方法是处理该问题的正确解决方案。或者,根本不分配默认的FileName at all,将其留空。如果用户没有指定明确的FileName,如果需要,您的代码可以采用默认值。

    另一方面,Integer 属性可以使用用户定义的默认值进行定义。不过,您的 Size 属性并没有这样做。应该是(特别是如果您将默认值的分配移到TConfiguration 构造函数中):

    property Size: Integer read FSize write SetSize default 10;
    
  3. 您的TConfiguration.Assign() 方法实施不正确。通过在复制值之前调用继承的Assign() 方法,如果调用者将一个TConfiguration 对象分配给另一个对象,您的代码将始终在运行时引发EConvertError 异常。原因是因为基类TPersistent.Assign()实现只调用了Source.AssignTo(Self),而TConfiguration没有覆盖AssignTo()方法,所以TPersistent.AssignTo()被调用了,它只是调用了Dest.AssignError(Self),引发了异常。因此,如果 Source 实际上是 TConfiguration 对象,则不要调用继承的 Assign() 方法:

    procedure TConfiguration.Assign(Source: TPersistent);
    begin
      if Source is TConfiguration then
      begin
        FDelay := TConfiguration(Source).Delay;
        FSize := TConfiguration(Source).Size;
      end else
        inherited;
    end;
    

【讨论】:

  • 为什么要将 TConfiguration 赋值从 TCustomClass 构造函数移动到 TConfiguration 构造函数?编写组件类时是常规方式吗?因为如果将来我创建另一个组件并使用相同的 TConfiguration 类,我该怎么办?
  • 一个类的属性是该类的一个特征。该属性的默认值也是。这已经是足够的理由了。但特别是当您需要在多个场景中使用该类时,您应该将其设计为能够自主运行。因此,您可以在需要它的类中创建一个TConfiguration,并在那里分配自定义设置,但默认设置是在TConfiguration 本身内进行的。
  • 这完全取决于您。如果一个青苹果需要在水果篮里变成红色,那么在装满篮子的时候让它变成红色。但是如果一个青苹果应该有红色斑点,那么你应该在制作篮子之前设计一个能够有斑点的斑点苹果。或者:如果篮子里装的是哪种香蕉无关紧要,则在装满篮子后注册香蕉类型。但是当篮子里可能只有 Chiquita 香蕉时,请通过专门为这种类型的篮子设计来自 TBanana 的 TChiquitaBanana 来强制执行该限制。
【解决方案2】:

但是,这只会将 TCustomClass Configuration 属性颜色设置为正常蓝色,而不是其属性。

这是设计使然。 storage specifier 仅适用于它指定的属性。

将您的类与表单上的TFont 属性进行比较。当ParentFontTrue 时,Font 属性将不会被存储。但是它的成员ColorName 仍然不是默认的,因此它们看起来好像它们将被存储。这是因为TFont 对象不知道它的所有者,它怎么可能呢?一次是画布的一部分,另一次是控件的一部分,或者根本没有所有者。在所有这些情况下,TFont 对象应该是功能齐全的,所以它不会询问它的父对象它应该如何表现。另一方面:父母也不应该询问孩子的行为方式。

现在回到你的场景:FileNameSize属性是否需要存储应该在TConfiguration中处理。是否应该存储TCustomClassConfiguration 属性可能取决于这些FileNameSize 属性。如果Configuration 属性被存储(粗体),但它的所有成员都没有被存储(不是粗体),那么最后什么都没有存储。如果你有一个ParentConfiguration 属性,就像ParentFont,那么你可以决定不存储Configuration 属性。否则,就这样吧,不要让大胆的举动进一步分散你的注意力。

【讨论】:

  • 你的话是真的。但在那篇引文中,我试图解释“存储”“配置”属性方法不足以满足我的需求,因为它只会使“配置”正常蓝色而不是它的属性 - 将保持灰色蓝色。
猜你喜欢
  • 2010-11-23
  • 2015-09-13
  • 1970-01-01
  • 2016-08-15
  • 1970-01-01
  • 2013-05-30
  • 2012-07-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多