Delphi:类中的记录
以下情况:
type
TRec = record
Member : Integer;
end;
TMyClass = class
private
FRec : TRec;
public
property Rec : TRec read FRec write FRec;
end;
以下内容不起作用(无法分配左侧),这没关系,因为 TRec
是值类型:
MyClass.Rec.Member := 0;
在 D2007 中,尽管以下内容确实有效:
with MyClass.Rec do
Member := 0;
不幸的是,它不起作用在 D2010 中(我认为它在 D2009 中也不起作用)。第一个问题:这是为什么?是故意改变的吗?或者这只是其他变化的副作用? D2007 解决方法只是一个“bug”吗?
第二个问题:您对以下解决方法有何看法?使用安全吗?
with PRec (@MyClass.Rec)^ do
Member := 0;
我在这里讨论的是现有代码,因此要使其工作而必须进行的更改应该是最小的。
谢谢!
Following situation:
type
TRec = record
Member : Integer;
end;
TMyClass = class
private
FRec : TRec;
public
property Rec : TRec read FRec write FRec;
end;
The following doesn't work (left side cannot be assigned to), which is okay since TRec
is a value type:
MyClass.Rec.Member := 0;
In D2007 though the following DOES work:
with MyClass.Rec do
Member := 0;
Unfortunately, it doesn't work in D2010 (and I assume that it doesn't work in D2009 either). First question: why is that? Has it been changed intentionally? Or is it just a side effect of some other change? Was the D2007 workaround just a "bug"?
Second question: what do you think of the following workaround? Is it safe to use?
with PRec (@MyClass.Rec)^ do
Member := 0;
I'm talking about existing code here, so the changes that have to be made to make it work should be minimal.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
不
编译是设计使然。事实上,两个“with”结构的编译(AFAICT)仅仅是一个疏忽。因此,两者都不“可以安全使用”。
两个安全的解决方案是:
MyClass.Rec
分配给您操作的临时记录,然后分配回MyClass.Rec
。TMyClass.Rec.Member
公开为其自身的属性。That
doesn't compile is by design. The fact that the both "with"-constructs ever compiled was (AFAICT) a mere oversight. So both are not "safe to use".
Two safe solution are:
MyClass.Rec
to a temporary record which you manipulate and assign back toMyClass.Rec
.TMyClass.Rec.Member
as a property on its own right.在像这样的情况下,类的记录需要“直接操作”,我经常采用以下方法:
这样做的好处是,您可以利用 Delphi 自动取消引用来使可读代码访问每个记录元素,即:
MyObject .MyRec.MyNum := 123;
我不记得了,但也许WITH适用于这种方法——我尽量不使用它!
布莱恩
In some situtations like this where a record of a class needs 'direct manipulation' I've often resorted to the following:
The benefit of this is that you can leverage the Delphi automatic dereferencing to make readable code access to each record element viz:
MyObject.MyRec.MyNum := 123;
I cant remember, but maybe WITH works with this method - I try not to use it!
Brian
不能直接赋值的原因是这里。
至于WITH,它仍然可以在D2009中工作,我希望它也可以在D2010中工作(我现在无法测试)。
更安全的方法是直接公开记录属性,如 Allen 在上述 SO 帖子:
The reason why it can't be directly assigned is here.
As for the WITH, it still works in D2009 and I would have expected it to work also in D2010 (which I can't test right now).
The safer approach is exposing the record property directly as Allen suggesed in the above SO post:
记录是值,它们并不意味着是实体。
它们甚至具有按副本分配的语义!这就是为什么您无法就地更改属性值的原因。因为它会违反 FRec 的值类型语义,并破坏依赖它不可变或至少是安全副本的代码。
他们的问题是,为什么你需要一个值(你的 TRec)来表现得像一个对象/实体?
无论如何,如果这就是您使用它的目的,那么将“TRec”作为一个类不是更合适吗?
我的观点是,当你开始使用超出其意图的语言功能时,你很容易发现自己处于必须一路与你的工具作斗争的境地。
Records are values, they aren't meant to be entities.
They even have assignment-by-copy semantics! Which is why you can't change the property value in-place. Because it would violate the value type semantics of FRec and break code that relied on it being immutable or at least a safe copy.
They question here is, why do you need a value (your TRec) to behave like an object/entity?
Wouldn't it be much more appropriate for "TRec" to be a class if that is what you are using it for, anyways?
My point is, when you start using a language feature beyond its intent, you can easily find yourself in a situation where you have to fight your tools every meter on the way.
它被更改的原因是它是一个编译器错误。它编译的事实并不能保证它会起作用。
一旦将 Getter 添加到属性中,它就会失败。
您的代码会突然中断,您会首先责怪 Delphi/Borland 允许这样做。
如果您无法直接分配属性,请不要使用 hack 来分配它 - 有一天它会反噬。
使用 Brian 的建议返回一个指针,但删除 With - 你可以轻松执行 Point.X := 10;
The reason it has been changed is that it was a compiler bug. The fact that it compiled didn't guarantee that it would work.
It would fail as soon as a Getter was added to the property
You code would suddenly break, and you'd blame Delphi/Borland for allowing it in the first place.
If you can't directly assign a property, don't use a hack to assign it - it will bite back someday.
Use Brian's suggestion to return a pointer, but drop the With - you can eaisly do Point.X := 10;
另一个解决方案是使用辅助函数:
尽管它仍然不安全(请参阅 Barry Kelly 对 Getter/Setter 的评论)
/编辑:下面是最丑陋的黑客(也可能是最不安全的),但它太有趣了,我不得不发布它:
它假设记录的每个成员都是(P)整数大小,如果不是,AV 将崩溃。
您甚至可以将偏移量硬编码为常量并直接通过偏移量进行访问
Another solution is to use a helper function:
It's still not safe though (see Barry Kelly's comment about Getter/Setter)
/Edit: Below follows the most ugly hack (and probably the most unsafe as well) but it was so funny I had to post it:
It assumes that every member of the record is (P)Integer sized and will crash of AV if not.
You could even hardcode the offsets as constants and access directly by offset