“左侧不能分配给” Delphi 中的记录类型属性
我很好奇为什么 Delphi 将记录类型属性视为只读:
TRec = record
A : integer;
B : string;
end;
TForm1 = class(TForm)
private
FRec : TRec;
public
procedure DoSomething(ARec: TRec);
property Rec : TRec read FRec write FRec;
end;
如果我尝试将值分配给 Rec 属性的任何成员,我会收到“左侧无法分配给”错误:
procedure TForm1.DoSomething(ARec: TRec);
begin
Rec.A := ARec.A;
end;
同时执行相同操作允许使用底层字段:
procedure TForm1.DoSomething(ARec: TRec);
begin
FRec.A := ARec.A;
end;
对该行为有任何解释吗?
I'm curious to know why Delphi treats record type properties as read only:
TRec = record
A : integer;
B : string;
end;
TForm1 = class(TForm)
private
FRec : TRec;
public
procedure DoSomething(ARec: TRec);
property Rec : TRec read FRec write FRec;
end;
If I try to assign a value to any of the members of Rec property, I'll get "Left side cannot be assigned to" error:
procedure TForm1.DoSomething(ARec: TRec);
begin
Rec.A := ARec.A;
end;
while doing the same with the underlying field is allowed:
procedure TForm1.DoSomething(ARec: TRec);
begin
FRec.A := ARec.A;
end;
Is there any explanation for that behavior?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
由于“Rec”是一个属性,编译器对它的处理方式略有不同,因为它必须首先评估属性 decl 的“读取”。 考虑一下这一点,这在语义上等同于您的示例:
如果您像这样查看它,您可以看到对“Rec”的第一个引用(在点“.”之前)必须调用 GetRec,这将创建一个临时本地Rec 的副本。 这些临时对象被设计为“只读”。 这就是你遇到的情况。
您可以在此处执行的另一件事是将记录的各个字段分解为包含类的属性:
这将允许您通过属性直接分配给类实例中嵌入记录的字段。
Since "Rec" is a property, the compiler treats it a little differently because it has to first evaluate the "read" of the property decl. Consider this, which is semantically equivalent to your example:
If you look at it like this, you can see that the first reference to "Rec" (before the dot '.'), has to call GetRec, which will create a temporary local copy of Rec. These temporaries are by design "read-only." This is what you're running into.
Another thing you can do here is to break out the individual fields of the record as properties on the containing class:
This will allow you to directly assign through the property to the field of that embedded record in the class instance.
是的,这是一个问题。 但问题可以使用记录属性来解决:
这可以毫无问题地编译和工作。
Yes this is a problem. But the problem can be solved using record properties:
This compiles and workes without problem.
我经常使用的解决方案是将属性声明为指向记录的指针。
这样,直接分配
Form1.Rec.A := MyInteger
就可以了,而且Form1.Rec := MyRec
也可以通过复制MyRec 中的所有值来工作
按预期添加到FRec
字段。这里唯一的陷阱是,当您希望实际检索要使用的记录的副本时,您将不得不使用类似
MyRec := Form1.Rec^
A solution I frequently use is to declare the property as a pointer to the record.
With this, directly assigning
Form1.Rec.A := MyInteger
will work, but alsoForm1.Rec := MyRec
will work by copying all the values inMyRec
to theFRec
field as expected.The only pitfall here is that when you wish to actually retrieve a copy of the record to work with, you will have to something like
MyRec := Form1.Rec^
编译器阻止您分配给临时变量。 C# 中的等效项是允许的,但没有效果; Rec 属性的返回值是基础字段的副本,并且分配给副本上的字段是 nop。
The compiler is stopping you from assigning to a temporary. The equivalent in C# is permitted, but it has no effect; the return value of the Rec property is a copy of the underlying field, and assigning to the field on the copy is a nop.
因为您有隐式 getter 和 setter 函数,并且您无法修改函数的 Result,因为它是 const 参数。
(注意:如果您转换对象中的记录,结果实际上是一个指针,因此相当于 var 参数)。
如果您想保留记录,则必须使用中间变量(或字段变量)或使用WITH 语句。
使用显式 getter 和 setter 函数查看以下代码中的不同行为:
Because you have implicit getter and setter functions and you cannot modify the Result of a function as it is a const parameter.
(Note: In case you transform the record in an object, the result would actually be a pointer, thus equivalent to a var parameter).
If you want to stay with a Record, you have to use an intermediate variable (or the Field variable) or use a WITH statement.
See the different behaviors in the following code with the explicit getter and setter functions:
最简单的方法是:
The simplest approach is:
这是因为属性实际上是作为函数编译的。 属性仅返回或设置一个值。 它不是指向记录的引用或指针
,因此:
与调用这样的函数相同:
您可以做的是:
它有点混乱,但是是这种类型的体系结构所固有的。
This is because property are actually complied as a function. Properties only return or set a value. It is not a reference or a pointer to the record
so :
is same as calling a function like this:
what you can do is:
It is a bit messy but inherent in this type of architecture.
正如其他人所说 - 读取属性将返回记录的副本,因此字段的分配不会作用于 TForm1 拥有的副本。
另一种选择是这样的:
Delphi 将为您取消引用 PRec 指针,因此这样的事情仍然有效:
不需要属性的写入部分,除非您想交换 FRec 指向的 PRec 缓冲区。 无论如何,我真的不建议通过属性进行此类交换。
Like others have said - the read property will return a copy of the record, so the assignment of fields isn't acting on the copy owned by TForm1.
Another option is something like:
Delphi will dereference the PRec pointer for you, so things like this will still work:
There's no need for a write part of the property, unless you want to swap the PRec buffer that FRec points at. I really wouldn't suggest to do such swapping via a property anyway.