我想将记录作为对象的属性。问题是,当我更改此记录的某个字段时,该对象不知道更改。
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'?也许在如何申报记录方面有一些技巧?
更多信息
我的目标是避免使用TObject
事件(例如TPersistent
或OnChange
)创建TFont
或TStringList
。我不熟悉为此使用对象,但是为了尝试清理我的代码,我看到我是否可以使用Record。我只需要确保在设置其中一个记录字段时可以正确调用我的记录属性设置器。
答案 0 :(得分:11)
考虑这一行:
MyObject.SomeRecord.SomeField := NewValue;
这实际上是编译错误:
[DCC错误]:E2064左侧无法分配到
您的实际代码可能是这样的:
MyRecord := MyObject.SomeRecord;
MyRecord.SomeField := NewValue;
这里发生的是您将记录类型的值复制到本地变量MyRecord
。然后,您可以修改此本地副本的字段。这不会修改MyObject中保存的记录。为此,您需要调用属性设置器。
MyRecord := MyObject.SomeRecord;
MyRecord.SomeField := NewValue;
MyObject.SomeRecord := MyRecord;
或切换到使用引用类型,即类,而不是记录。
总而言之,您当前代码的问题是没有调用SetSomeRecord,而是您正在修改记录的副本。这是因为记录是值类型,而不是引用类型。
答案 1 :(得分:11)
最终,您将需要访问记录的字段,但正如您所建议的那样,记录通常是类中适合的抽象选择。类可以整齐地访问记录的属性,如下所示:
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;
注意:
希望这有帮助。
答案 2 :(得分:4)
如何使用TObject而不是Record?
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;
您现在可以读取和设置TMyProperties的属性,如下所示:
MyObject.MyProperties.SomeField := 1;
x := MyObject.MyProperties.SomeField;
使用此方法,您无需单独获取/设置记录的值。如果需要捕获FMyProperties中的更改,可以在属性声明中添加“set”过程。
答案 3 :(得分:4)
为什么不让记录器的setter / getter成为一部分呢?
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;
答案 4 :(得分:3)
您按值传递记录,因此对象存储记录的副本。从那时起,实际上有两个对象;原始的和对象持有的副本。改变一个不会改变另一个。
您需要通过引用传递记录。
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;
答案 5 :(得分:1)
这是@ 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 be assigned to"
。