Bal*_*ric 20 delphi with-statement
我听过很多程序员,特别是Delphi程序员嘲笑使用'with'.
我认为它使程序运行得更快(只有一个对父对象的引用),并且如果使用得当,它更容易阅读代码(少于十几行代码并且没有嵌套).
这是一个例子:
procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
with ARect do FillRectS(Left, Top, Right, Bottom, Value);
end;
Run Code Online (Sandbox Code Playgroud)
我喜欢用with
.我怎么了?
Lar*_*ens 33
使用with的一个烦恼是调试器无法处理它.因此它使调试更加困难.
更大的问题是它不容易阅读代码.特别是如果with语句有点长.
procedure TMyForm.ButtonClick(...)
begin
with OtherForm do begin
Left := 10;
Top := 20;
CallThisFunction;
end;
end;
Run Code Online (Sandbox Code Playgroud)
将调用哪个Form的CallThisFunction?自我(TMyForm)或其他形式?如果不检查OtherForm是否具有CallThisFunction方法,您无法知道.
而最大的问题是,你甚至可以在不知情的情况下轻松搞错.如果TMyForm和OtherForm都有CallThisFunction,但它是私有的,那该怎么办?您可能希望/希望调用OtherForm.CallThisFunction,但实际上并非如此.如果你没有使用with,编译器会警告你,但现在却没有.
在with中使用多个对象会使问题倍增.请参见http://blog.marcocantu.com/blog/with_harmful.html
Kon*_*lph 12
在这种情况下我更喜欢VB语法,因为在这里,你需要在with块内的成员前加一个.
以避免含糊不清:
With obj
.Left = 10
.Submit()
End With
Run Code Online (Sandbox Code Playgroud)
但实际上,with
一般来说没有任何问题.
mar*_*_ja 11
如果with
通过以下方式扩展声明将会很棒:
with x := ARect do
begin
x.Left := 0;
x.Rigth := 0;
...
end;
Run Code Online (Sandbox Code Playgroud)
您不需要声明变量'x'.它将由编译器创建.它写得快,没有混淆,使用了哪个功能.
"with"不太可能使代码运行得更快,编译器更有可能将其编译为相同的可执行代码.
人们不喜欢"with"的主要原因是它可能会引入关于命名空间范围和优先级的混淆.
有些情况下,这是一个真正的问题,以及这是一个非问题的情况(非问题情况将在问题中描述为"明智地使用").
由于可能存在混淆,一些开发人员选择完全避免使用"with",即使在可能没有这种混淆的情况下也是如此.这可能看起来很教条,但可以说,随着代码的变化和增长,即使在代码被修改到一定程度上会使"与"混淆之后,"with"的使用仍然可能仍然存在,因此最好不要首先介绍它的用途.
事实上:
procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
with ARect do FillRectS(Left, Top, Right, Bottom, Value);
end;
Run Code Online (Sandbox Code Playgroud)
和
procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
FillRectS(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom, Value);
end;
Run Code Online (Sandbox Code Playgroud)
将生成完全相同的汇编程序代码.
如果with
子句的值是函数或方法,则可能存在性能损失.在这种情况下,如果你想要有良好的维护和良好的速度,只需执行编译器在场景后面做的事情,即创建一个临时变量.
事实上:
with MyRect do
begin
Left := 0;
Right := 0;
end;
Run Code Online (Sandbox Code Playgroud)
由编译器以伪代码编码:
var aRect: ^TRect;
aRect := @MyRect;
aRect^.Left := 0;
aRect^.Right := 0;
Run Code Online (Sandbox Code Playgroud)
然后aRect
可以只是一个CPU寄存器,但也可以是堆栈上的真正临时变量.当然,我在这里使用指针,因为TRect
是record
.它对于对象更直接,因为它们已经是指针.
就个人而言,我有时会在我的代码中使用,但我几乎每次都会检查asm,以确保它能够完成应有的操作.不是每个人都能够或有时间去做,所以恕我直言,本地变量是一个很好的选择.
我真的不喜欢这样的代码:
for i := 0 to ObjList.Count-1 do
for j := 0 to ObjList[i].NestedList.Count-1 do
begin
ObjList[i].NestedList[j].Member := 'Toto';
ObjList[i].NestedList[j].Count := 10;
end;
Run Code Online (Sandbox Code Playgroud)
它仍然具有以下可读性:
for i := 0 to ObjList.Count-1 do
for j := 0 to ObjList[i].NestedList.Count-1 do
with ObjList[i].NestedList[j] do
begin
Member := 'Toto';
Count := 10;
end;
Run Code Online (Sandbox Code Playgroud)
甚至
for i := 0 to ObjList.Count-1 do
with ObjList[i] do
for j := 0 to NestedList.Count-1 do
with NestedList[j] do
begin
Member := 'Toto';
Count := 10;
end;
Run Code Online (Sandbox Code Playgroud)
但如果内部循环很大,那么局部变量确实有意义:
for i := 0 to ObjList.Count-1 do
begin
Obj := ObjList[i];
for j := 0 to Obj.NestedList.Count-1 do
begin
Nested := Obj.NestedList[j];
Nested.Member := 'Toto';
Nested.Count := 10;
end;
end;
Run Code Online (Sandbox Code Playgroud)
这段代码不会慢于with
:编译器确实在场景后面做了!
顺便说一句,它将允许更容易的调试:你可以放置一个断点,然后指向鼠标Obj
或Nested
直接获取内部值.
归档时间: |
|
查看次数: |
7538 次 |
最近记录: |