Delphi属性的真实示例在哪里?
例如,我知道我们可以使用“new”2010 attributes特性在运行时将数据库表字段序列化为对象属性,而我不是这方面的专家,因此我研究了TMS源代码,但我自己不知道如何实现它,不是针对DB,也不是针对XML 所以我在Delphi属性的真实示例在哪里?,delphi,oop,attributes,rtti,Delphi,Oop,Attributes,Rtti,例如,我知道我们可以使用“new”2010 attributes特性在运行时将数据库表字段序列化为对象属性,而我不是这方面的专家,因此我研究了TMS源代码,但我自己不知道如何实现它,不是针对DB,也不是针对XML 所以我在Delphi Attributes上搜索了谷歌所有的搜索结果,人们发布的所有结果都是声明示例,然后在展示示例之前就停止了 那么,我们如何在表单/执行代码中投影、声明、编码和使用这些丰富的类的真实示例在哪里呢 有没有人可以在这里分享一个例子,或者知道一篇完整的好文章 Edit1:
Delphi Attributes
上搜索了谷歌所有的搜索结果,人们发布的所有结果都是声明示例,然后在展示示例之前就停止了
那么,我们如何在表单/执行代码中投影、声明、编码和使用这些丰富的类的真实示例在哪里呢
有没有人可以在这里分享一个例子,或者知道一篇完整的好文章
Edit1:
答案应该有一个
t表单
,带有一个t按钮
,单击该按钮时,执行对所创建属性类的一些使用,不要只显示属性和类接口,因为正如我之前所说的,有很多声明示例,我必须说,我不太清楚您需要什么样的示例。IMHO是您应该需要的一切,也许前提是您具备一些注释和/或方面编程的基本知识
示例取决于特定软件作者使用属性的方式/目的。您提到了ORM系统:这里的典型用法是用此类框架后端的DB操作所需的附加信息注释表示DB实体的类的成员。假设您有一个DB实体,其字段COMPANY CHAR(32)不为NULL,并且希望在Delphi类中表示它:
TSomeDBEntity = class(...)
FCDS: TClientDataset;
...
constructor Create;
...
[TCharColumn('COMPANY', 32, false)]
property CompanyName: string read GetCompanyName write SetCompanyName;
end;
然后,您将使用构造函数定义属性TCharColumn
constructor TCharColumn.Create(const AFieldName:string; ALength:integer; ANullable:boolean);
begin
inherited;
FName := AFieldName;
FLength := ALength;
FNullable := ANullable;
end;
这种注释的用法可能如下所示:
FCDS := TClientDataset.Create(nil);
RttiContext := TRttiContext.Create;
try
RttiType := RttiContext.GetType(self.ClassType);
Props := RttiType.GetProperties;
for Prop in Props do
begin
Attrs := Prop.GetAttributes;
case Prop.PropertyType.TypeKind of
tkUString:
begin
for Attr in Attrs do
if Attr is TCharColumn then
begin
ColAttr := TCharColumn(Attr);
FCDS.FieldDefs.Add(ColAttr.FName, ftString, ColAttr.FLength, not ColAttr.FNullable);
end;
end;
else
//... ;
end;
end;
finally
RttiContext.Free;
end;
type
TDisplayLabelAttribute = class(TCustomAttribute)
private
FText: string;
public
constructor Create(const aText: string);
property Text: string read FText write FText;
end;
这段程序演示了如何在运行时基于Delphi中的注释定义数据集中的字段。由于缺少命名参数,我们受到了一些限制,因此使用参数列表并不像在Java中那样灵活(比较TMS Aurelius注释集和不确定问题是询问属性使用的真实示例还是如何使用属性将db表序列化为对象。下面的示例是一个精心设计的简单示例(但仍然是一个示例),显示了如何使用属性记录对象属性的更改 定义自定义属性
//By convention attributes are *not* prefixed with a `T`
//and have the word `Attribute` in their name
LoggableAttribute = class(TCustomAttribute)
private
FDescription : String;
public
constructor Create(Description: String);
property Description: String read FDescription;
end;
使用属性的TProduct类的“hello world”
TProduct = Class(TObject)
private
FPrice: Double;
FDescription: String;
..
public
[LoggableAttribute('Product Price')]
property Price : Double read FPrice write SetPrice;
[Loggable('Product Description')] {the `Attribute` part is optional}
property Description : String read FDescription write SetDescription;
property IsDirty : Boolean read FIsDirty;
End;
任何具有“loggable属性”的类都可以传递到此方法,以迭代属性并记录它们
procedure LogChanges(LoggableClass: TObject);
var
c : TRttiContext;
t : TRttiType;
p : TRttiProperty;
a : TCustomAttribute;
Value : TValue;
begin
c := TRttiContext.Create;
try
t := c.GetType(LoggableClass.ClassType);
for p in t.getProperties do
for a in p.GetAttributes do
if a is TLoggableProperty then begin
Value := p.GetValue(LoggableClass);
// log to db..
AddLogEntry(p.Name, TLoggableProperty(a).Description, Value.ToString);
end;
finally
c.Free;
end;
结束
使用示例:
var
P : TProduct;
begin
P := TProduct.Create;
P.LoadPropertiesFromDB;
...
... User edits price ...
...
P.Price := 499.99;
...
... Save product to DB
if P.IsDirty then // save and log
LogChanges(P);
如果要声明自己的属性,可以如下操作:
FCDS := TClientDataset.Create(nil);
RttiContext := TRttiContext.Create;
try
RttiType := RttiContext.GetType(self.ClassType);
Props := RttiType.GetProperties;
for Prop in Props do
begin
Attrs := Prop.GetAttributes;
case Prop.PropertyType.TypeKind of
tkUString:
begin
for Attr in Attrs do
if Attr is TCharColumn then
begin
ColAttr := TCharColumn(Attr);
FCDS.FieldDefs.Add(ColAttr.FName, ftString, ColAttr.FLength, not ColAttr.FNullable);
end;
end;
else
//... ;
end;
end;
finally
RttiContext.Free;
end;
type
TDisplayLabelAttribute = class(TCustomAttribute)
private
FText: string;
public
constructor Create(const aText: string);
property Text: string read FText write FText;
end;
属性是一个常规类,它的祖先是TCustomAttribute。您可以像往常一样实现它:
implementation
constructor TDisplayLabelAttribute.Create(const aText: string);
begin
FText := aText;
end;
现在该属性已声明并实现,您只需使用它:
[DisplayLabel('My Class')]
TMyClass = class
end;
因此,现在您已经声明并实现了一个属性,并使用它向某个类添加了一个显示标签。最后一个阶段是使用该属性,因为您有一个用它修饰的类。使用该属性的代码既不驻留在该属性中,也不驻留在修饰的类中,而是在将使用该属性的服务层中实现装饰
假设我们有一个类,它为一个类返回一个可能的显示标签:
type
TArtifactInspector = class
public
class function DisplayLabelFor(aClass: TClass): string;
end;
如果类存在,该方法将检查该类并返回其显示标签。否则,它将返回空字符串:
implementation
uses
Rtti;
class function TArtifactInspector.DisplayLabelFor(aClass: TClass): string;
var
rttiContext: TRttiContext;
rttiType: TRttiType;
attribute: TCustomAttribute;
begin
rttiContext := TRttiContext.Create;
try
rttiType := rttiContext.GetType(aClass);
for attribute in rttiType.GetAttributes do
if attribute is TDisplayLabelAttribute then
Exit(TDisplayLabelAttribute(attribute).Text);
Result := '';
finally
rttiContext.Free;
end; // try to recover and return the DisplayLabel
end;
有些文本中,作者费心创建一个完整的文件,其中包含属性方法的声明和构造以及有趣的属性使用,但没有给出它的用法示例。我最感兴趣的是——不久前遇到同样的“问题”,从未使用过属性,因为我看不到它的好处。我认为根本的问题是,太多长期的Delphi程序员习惯于没有他们,以至于我们还没有发现他们在很多地方比我们的工作方式有明显的优势。对于许多第三方组件供应商来说,广泛使用属性会阻止他们将目标锁定在旧Delphi版本的用户limi这取决于你的问题领域:典型的应用程序程序员可能不会定义他/她的属性。主要的,这是使用附加信息(元数据)操作的工具在开发通用的ORM框架时,与运行时的传统数据类型相关联。从我在TMS ORM站点看到的情况来看,代码一开始不知道选项卡的哪些字段,因此当我们使用
Create
方法时,框架将读取数据库并允许我们键入MyString:=MyORM.TableName.TableField;
在您的示例中,您必须声明[TCharColumn('COMPANY',32,false)]
。我是否误解了属性的真正用途?创建DB表的传统方法是编写SQL命令create table,并使用SQL对此类表进行操作。在ORM中,您不使用SQL命令(where子句除外)。DB table是注释类(包含属性),您可以通过ORM操作数据。我不知道TMS,但例如启动时Hibernate检查如何处理RDBMS中有关注释的DB模式:验证:生产,在diff上引发异常,更新:尝试在RDBMS中更新DB模式(并非所有内容都可以更新),create,create drop:删除现有数据库(包括数据)并创建新的数据库模式可能我应该把问题改为“如何使用属性将数据库表序列化为对象”…:-)按照惯例,属性前面没有T
。此外,可以在注释中省略名称的属性
部分。因此,可以像这样使用名为LoggableAttribute
的属性: