当前位置:  开发笔记 > 后端 > 正文

为什么我不能在Delphi中使用"with"?

如何解决《为什么我不能在Delphi中使用"with"?》经验,为你挑选了5个好方法。

我听过很多程序员,特别是Delphi程序员嘲笑使用'with'.

我认为它使程序运行得更快(只有一个对父对象的引用),并且如果使用得当,它更容易阅读代码(少于十几行代码并且没有嵌套).

这是一个例子:

procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
  with ARect do FillRectS(Left, Top, Right, Bottom, Value);
end;

我喜欢用with.我怎么了?



1> Lars Truijen..:

使用with的一个烦恼是调试器无法处理它.因此它使调试更加困难.

更大的问题是它不容易阅读代码.特别是如果with语句有点长.

procedure TMyForm.ButtonClick(...)
begin
  with OtherForm do begin
    Left := 10;
    Top := 20;
    CallThisFunction;
  end;
end;

将调用哪个Form的CallThisFunction?自我(TMyForm)或其他形式?如果不检查OtherForm是否具有CallThisFunction方法,您无法知道.

而最大的问题是,你甚至可以在不知情的情况下轻松搞错.如果TMyForm和OtherForm都有CallThisFunction,但它是私有的,那该怎么办?您可能希望/希望调用OtherForm.CallThisFunction,但实际上并非如此.如果你没有使用with,编译器会警告你,但现在却没有.

在with中使用多个对象会使问题倍增.请参见http://blog.marcocantu.com/blog/with_harmful.html



2> Konrad Rudol..:

在这种情况下我更喜欢VB语法,因为在这里,你需要在with块内的成员前加一个.以避免含糊不清:

With obj
    .Left = 10
    .Submit()
End With

但实际上,with一般来说没有任何问题.


同意.这基本上是我唯一比VB更喜欢Delphi的东西.
这不适用于嵌套的"with".(不管你喜欢与否.)

3> markus_ja..:

如果with通过以下方式扩展声明将会很棒:

with x := ARect do
begin
  x.Left := 0;
  x.Rigth := 0;
  ...
end;

您不需要声明变量'x'.它将由编译器创建.它写得快,没有混淆,使用了哪个功能.


在XE2和更新版本中只需调用:`x.Create(left,top,right,bottom);`.不再需要使用`with`了.许多标准类型都使用`高级记录'类型实现.

4> Graza..:

"with"不太可能使代码运行得更快,编译器更有可能将其编译为相同的可执行代码.

人们不喜欢"with"的主要原因是它可能会引入关于命名空间范围和优先级的混淆.

有些情况下,这是一个真正的问题,以及这是一个非问题的情况(非问题情况将在问题中描述为"明智地使用").

由于可能存在混淆,一些开发人员选择完全避免使用"with",即使在可能没有这种混淆的情况下也是如此.这可能看起来很教条,但可以说,随着代码的变化和增长,即使在代码被修改到一定程度上会使"与"混淆之后,"with"的使用仍然可能仍然存在,因此最好不要首先介绍它的用途.


在优化一些经常调用的代码时,我曾经做过一些非常有限的测试.它没有改善运行时间.如果这是一个问题,比如数组查找,那么临时变量就是解决方案.编译器非常聪明,通常最好不要管它.

5> Arnaud Bouch..:

事实上:

procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
  with ARect do FillRectS(Left, Top, Right, Bottom, Value);
end;

procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
  FillRectS(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom, Value);
end;

将生成完全相同的汇编程序代码.

如果with子句的值是函数或方法,则可能存在性能损失.在这种情况下,如果你想要有良好的维护和良好的速度,只需执行编译器在场景后面做的事情,即创建一个临时变量.

事实上:

with MyRect do
begin
  Left := 0;
  Right := 0;
end;

由编译器以伪代码编码:

var aRect: ^TRect;

aRect := @MyRect;
aRect^.Left := 0;
aRect^.Right := 0;

然后aRect可以只是一个CPU寄存器,但也可以是堆栈上的真正临时变量.当然,我在这里使用指针,因为TRectrecord.它对于对象更直接,因为它们已经是指针.

就个人而言,我有时会在我的代码中使用,但我几乎每次都会检查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;

它仍然具有以下可读性:

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;

甚至

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;

但如果内部循环很大,那么局部变量确实有意义:

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;

这段代码不会慢于with:编译器确实在场景后面做了!

顺便说一句,它将允许更容易的调试:你可以放置一个断点,然后指向鼠标ObjNested直接获取内部值.

推荐阅读
勤奋的瞌睡猪_715
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有