Delphi 记录可以用作对象的属性吗?
我想做一个记录作为对象的属性。问题是,当我更改此记录的一个字段时,对象没有意识到更改Delphi 记录可以用作对象的属性吗?,delphi,properties,delphi-xe2,Delphi,Properties,Delphi Xe2,我想做一个记录作为对象的属性。问题是,当我更改此记录的一个字段时,对象没有意识到更改 type TMyRecord = record SomeField: Integer; end; TMyObject = class(TObject) private FSomeRecord: TMyRecord; procedure SetSomeRecord(const Value: TMyRecord); public property SomeRec
type
TMyRecord = record
SomeField: Integer;
end;
TMyObject = class(TObject)
private
FSomeRecord: TMyRecord;
procedure SetSomeRecord(const Value: TMyRecord);
public
property SomeRecord: TMyRecord read FSomeRecord write SetSomeRecord;
end;
如果我这样做了
MyObject.SomeRecord.SomeField:= 5;
…行不通
那么,当记录的某个字段被写入时,如何使属性设置过程“catch”?也许是关于如何申报记录的一些技巧
TMyRecord = record
fFirstname: string;
procedure SetFirstName(AValue: String);
property
Firstname : string read fFirstname write SetFirstName;
end;
TMyClass = class(TObject)
MyRecord : TMyRecord;
end;
procedure TMyRecord.SetFirstName(AValue: String);
begin
// do extra checking here
fFirstname := AValue;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyClass: TMyClass;
begin
MyClass := TMyClass.Create;
try
MyClass.MyRecord.Firstname := 'John';
showmessage(MyClass.MyRecord.Firstname);
finally
MyClass.Free;
end;
end;
更多信息
我的目标是避免使用
OnChange
事件(例如TFont
或TStringList
)创建一个TObject
或t持久的
)。我非常熟悉使用对象来实现这一点,但为了稍微清理一下代码,我正在考虑是否可以使用记录来代替。我只需要确保在设置记录的一个字段时可以正确调用我的记录属性setter。您正在按值传递记录,因此对象存储了记录的副本。从那时起,实际上有两个目标;原件和由该对象持有的副本。改变一个不会改变另一个
您需要通过引用传递记录
type
TMyRecord = record
SomeField: Integer;
end;
PMyRecord = ^TMyRecord;
TMyObject = class(TObject)
private
FSomeRecord: PMyRecord;
procedure SetSomeRecord(const Value: PMyRecord);
public
property SomeRecord: PMyRecord read FSomeRecord write SetSomeRecord;
end;
用TObject代替记录怎么样
type
TMyProperties = class(TObject)
SomeField: Integer;
end;
TMyObject = class(TObject)
private
FMyProperties: TMyProperties;
public
constructor Create;
destructor Destroy; override;
property MyProperties: TMyRecord read FMyProperties;
end;
implementation
constructor TMyObject.Create;
begin
FMyProperties := TMyProperties.Create;
end;
destructor TMyObject.Destroy;
begin
FMyProperties.Free;
end;
现在,您可以像下面这样读取和设置TMYProperty的属性:
MyObject.MyProperties.SomeField := 1;
x := MyObject.MyProperties.SomeField;
MyRecord := MyObject.SomeRecord;
MyRecord.SomeField := NewValue;
使用此方法,不需要单独获取/设置记录中的值。如果需要捕获FmyProperty中的更改,可以在属性声明中添加“set”过程。考虑这一行:
MyObject.SomeRecord.SomeField := NewValue;
这实际上是一个编译错误:
[DCC错误]:E2064左侧无法分配给
您的实际代码可能如下所示:
MyObject.MyProperties.SomeField := 1;
x := MyObject.MyProperties.SomeField;
MyRecord := MyObject.SomeRecord;
MyRecord.SomeField := NewValue;
这里发生的是将记录类型的值复制到局部变量MyRecord
。然后修改此本地副本的字段。这不会修改MyObject中保存的记录。为此,需要调用属性设置器
MyRecord := MyObject.SomeRecord;
MyRecord.SomeField := NewValue;
MyObject.SomeRecord := MyRecord;
或者切换到使用引用类型,即类,而不是记录
总而言之,当前代码的问题在于没有调用SetSomeRecord,而是修改了记录的副本。这是因为记录是值类型,而不是引用类型,最终您会希望访问记录的字段,但正如您所建议的,记录通常是类中合适的抽象选择。类可以灵活地访问记录的属性,如下所示:
type
TMyRec = record
SomeRecInteger: integer;
SomeRecString: string;
end;
TMyClass = class(TObject)
private
FMyRec: TMyRec;
procedure SetSomeString(const AString: string);
public
property SomeInteger: integer read FMyRec.SomeRecInteger write FMyRec.SomeRecInteger;
property SomeString: string read FMyRec.SomeRecString write SetSomeString;
end;
procedure TMyClass.SetSomeRecString(const AString: string);
begin
If AString <> SomeString then
begin
// do something special if SomeRecString property is set
FMyRec.SomeRecString := AString;
end;
end;
类型
TMyRec=记录
SomeReceinter:整数;
SomeRecString:string;
结束;
TMyClass=类(TObject)
私有的
FMyRec:TMyRec;
过程SetSomeString(常数:string);
公众的
属性SomeInteger:整数读取FMyRec.someReceinter写入FMyRec.someReceinter;
属性SomeString:string read FMyRec.SomeRecString write SetSomeString;
结束;
过程TMyClass.SetSomeRecString(常数:字符串);
开始
如果你想要什么东西的话
开始
//如果设置了SomeRecString属性,请执行一些特殊操作
FMyRec.SomeRecString:=AString;
结束;
结束;
注:
希望这能有所帮助。为什么不让二传手/接球手成为记录的一部分呢
TMyRecord = record
fFirstname: string;
procedure SetFirstName(AValue: String);
property
Firstname : string read fFirstname write SetFirstName;
end;
TMyClass = class(TObject)
MyRecord : TMyRecord;
end;
procedure TMyRecord.SetFirstName(AValue: String);
begin
// do extra checking here
fFirstname := AValue;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
MyClass: TMyClass;
begin
MyClass := TMyClass.Create;
try
MyClass.MyRecord.Firstname := 'John';
showmessage(MyClass.MyRecord.Firstname);
finally
MyClass.Free;
end;
end;
这是@SourceMaid答案的另一种选择 您可以在对象内部使用记录(而不是指向记录的指针),并具有一个只读属性,该属性返回指向记录的指针 班级:
type
TMyRecord = record
I:Integer;
end;
PMyRecord = ^TMyRecord;
TMyObject = class
private
FMyRecord:TMyRecord;
function GetMyRecordPointer: PMyRecord;
public
property MyRecord: PMyRecord read GetMyRecordPointer;
end;
吸气剂:
function TMyObject.GetMyRecordPointer: PMyRecord;
begin
result := @FMyRecord;
end;
用法:
o := TMyObject.Create;
o.MyRecord.I := 42;
ShowMessage(o.MyRecord.I.ToString);
o.MyRecord.I := 23;
ShowMessage(o.MyRecord.I.ToString);
o.Free;
你不需要一个设定者,因为你得到了一个推荐人并与之合作。这意味着您不能通过分配新记录来更改整个记录。但是,您可以直接操作记录的元素,而不会出现错误
“Left side cannot assignment to”
。这使得类的使用非常困难,因为您强制类的客户端分配记录,并确保其生存期跟踪对象的生存期。当然,您的意思是将记录存储在类中,并公开用getter函数实现的只读指针属性。结果实现:=@FSomeRecord,其中FSomeRecord是TMyRecord。如果要这样做,基本上相当于将记录声明为类的公共字段,您也可以这样做,因为这样更容易理解。@DavidHeffernan同意,这只会带来更多的困难。+1为了方便起见,我还将添加一个接受TMyRec参数的过程,这样我就可以一次性设置所有值。+1不完全是我想要的,但教会了我一些新的东西:我从来都不知道可以使用记录的字段作为属性getter/setter。事实上,我经常使用对象(和persistents)。它通常还包括一些您没有的内容:OnChange事件。本质上,我的重点是能够捕获此记录(或对象)的更改在SetSomeRecord
过程中。我想看看是否可以将其封装到一个记录中,而不需要OnChange
事件。这是否是packed记录
可能派上用场的地方?packed
或其他方面是无关紧要的。这只是控制记录的布局和对齐。fundamental的问题是值类型和引用类型之间的区别。事实上,你问题中的代码甚至都没有编译。@DavidHeffernan非常正确,我实际上没有把代码放在Delphi中,而是直接在这里键入的。+1这意味着绝对没有办法让记录属性设置程序捕获对其字段的修改?糟糕er:(对于基于对象的属性也是如此,这就是为什么像TFont
,TStrings
等类有一个OnChange
事件-父组件为该事件分配一个内部事件处理程序,这样子属性的更改就可以被处理