我想创建一个结构如下的组件:
type
TCustomMyComp = class;
TMyComp = class;
TCustomSqlCommands = class;
TSqlCommands = class;
TFields = class;
TFieldsItem = class;
TFieldsSqlCommands = class;
TCustomMyComp = class(TComponent)
private
FFields: TFields;
FSqlCommands: TSqlCommands;
procedure SetFields(Value: TFields);
procedure SetSqlCommands(Value: TSqlCommands);
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Fields: TFields read FFields write SetFields;
property SqlCommands: TSqlCommands read FSqlCommands write SetSqlCommands;
end;
TCustomSqlCommands = class(TPersistent)
private
FOwner: TCustomMyComp;
FSelect: TStrings;
FInsert: TStrings;
FUpdate: TStrings;
FDelete: TStrings;
procedure SetSql(Index: Integer; Value: TStrings);
public
constructor Create(AOwner: TCustomMyComp); virtual;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
published
property Select: TStrings index 0 read FSelect write SetSql;
property Insert: TStrings index 1 read FInsert write SetSql;
property Update: TStrings index 2 read FUpdate write SetSql;
property Delete: TStrings index 3 read FDelete write SetSql;
end;
[ComponentPlatformsAttribute(pidWin32 or pidWin64)]
TMyComp = class(TCustomMyComp)
published
property Fields;
property SqlCommands;
end;
TSqlCommands = class(TCustomSqlCommands)
published
property Insert;
property Update;
property Delete;
end;
//------- Fields -----------
TFields = class(TCollection)
private
FOwner: TCustomMyComp;
function GetItem(Index: Integer): TFieldsItem;
procedure SetItem(Index: Integer; Value: TFieldsItem);
protected
function GetOwner: TPersistent; override;
procedure Update(Item: TCollectionItem); override;
public
constructor Create(AOwner: TCustomMyComp);
function Add: TFieldsItem;
function Owner: TCustomMyComp;
end;
TFieldsItem = class(TCollectionItem)
private
FOwner: TCustomMyComp;
FSqlCommands: TFieldsSqlCommands;
procedure SetSqlCommands(Value: TFieldsSqlCommands);
protected
function GetDisplayName: String; override;
procedure SetIndex(Value: Integer); override;
public
constructor Create(Collection: TCollection); override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
published
property SqlCommands: TFieldsSqlCommands read FSqlCommands write SetSqlCommands;
end;
TFieldsSqlCommands = class(TCustomSqlCommands)
published
property Select;
property Insert;
end;
我面临两个问题:
属性SqlCommands: TSqlCommands
包含TCustomSqlCommands
下定义的所有属性,即使我定义为仅发布Insert,Update,Delete
。
属性Fileds->SqlCommands: TFieldsSqlCommands
包含NO属性,即使我定义为仅发布Select,Insert
。
我做错了什么?
答案 0 :(得分:2)
典型的设计模式是让TCustom...
类将其属性定义为public
或protected
,但绝不定义为published
。这样,派生类可以根据需要决定要将哪些属性提升为published
。这将允许TSqlCommands
不发布Select
属性,TFieldsSqlCommands
不发布Update
和Delete
属性。
您的Fields
属性是TFields
对象(顺便说一句,VCL已在TFields
单元中有DB
个类,因此您应该考虑重命名上课到更独特的东西)。您的TFields
类没有SqlCommands
属性,而是TFieldsItem
的属性。因此,您需要访问其中一个字段项才能访问其SqlCommands
属性。
但是,尽管TFields
是TFieldsItem
个对象的集合,但您尚未声明属性以公开直接对TFieldsItem
对象指针的访问权限(尽管你已经为这样的属性声明了getter / setter方法)。您从Items[]
继承了基本TCollection
属性,该属性公开了TCollectionItem
对象指针,您必须将其输入到TFieldItem
。因此,您应该将自己的属性(将其命名为任意名称)添加到TFields
类以处理该类型转换:
TFields = class(TCollection)
private
...
function GetField(Index: Integer): TFieldsItem;
procedure SetField(Index: Integer; Value: TFieldsItem);
...
public
...
property Fields[Index: Integer]: TFieldsItem read GetField write SetField default;
end;
顺便说一句,由于TFields
有一个Owner
,您应该考虑直接从TOwnedCollection
而不是TCollection
派生。
答案 1 :(得分:1)
我们是在讨论代码中的可见性吗?如果是,那么您将所有属性视为公共属性是正确的,因为已发布的属性具有相同的可见性。这两者之间的唯一区别是已发布的属性包含在RTTI中,因此可以在Object Inspector中看到它们。但在代码中,您可以平等地访问公共和已发布的属性。
因此,如果您的第一个问题来自编码方面,这是正确的行为。
关于你的第二个问题:
如果从Fields
实例访问TMyCustomComp
属性,则不直接拥有SqlCommands
属性,因为您只能访问TFields
对象,继承自TCollection
。您首先必须访问其中一个项目,如果需要将其转换为TFieldsItem
,然后您可以访问其SqlCommands
属性。
在TCustomSqlCommands
中,您已发布了这些属性,并在TFieldsSqlCommands
中再次发布了这些属性。我不知道这是否是一个问题,但也许您可以尝试在TCustomSqlCommands
中将其公开,并将已发布的内容保存在TFieldsSqlCommands
中。但是,由于其可见性,您仍然可以在代码中访问它们。