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

时间:2020-03-05 18:55:41  来源:igfitidea点击:

我听说很多程序员,尤其是Delphi程序员都反对使用" with"。

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

这是一个例子:

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

我喜欢使用with。我怎么了

解决方案

回答

我不喜欢它,因为它使调试变得麻烦。我们不能仅通过使用鼠标悬停在变量上来读取变量等的值。

回答

Javascipt中也经常发生这种争论。

基本上,有了语法,很难一眼就知道要调用哪个Left / Top / etc属性/方法,就可以有一个名为Left的局部变量和一个属性(自从我已经有一段时间了完成了delphi,抱歉,如果名称错误)称为Left,甚至一个称为Left的函数。任何不熟悉ARect结构的人都可能会非常迷路。

回答

只要保持简单并避免歧义,它就没有任何问题。

据我所知,尽管它纯粹是语法糖,但它并没有加快任何速度。

回答

在这种情况下,我更喜欢使用VB语法,因为在这里,我们需要在with块中的成员前面加上`。

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

但是实际上,with没什么大不了。

回答

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

更大的问题是,阅读代码不太容易。尤其是在with语句较长的情况下。

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

哪个表格的CallThisFunction将被调用?自我(TMyForm)还是OtherForm?如果不检查OtherForm是否具有CallThisFunction方法,就无法知道。

最大的问题是,我们甚至可以在不知道的情况下轻松实现错误。如果TMyForm和OtherForm都具有CallThisFunction却是私有的,该怎么办。我们可能希望/希望调用OtherForm.CallThisFunction,但实际上不是。如果我们不使用with,编译器会警告我们,但现在不使用。

在with中使用多个对象会使问题成倍增加。看到http://blog.marcocantu.com/blog/with_harmful.html

回答

它允许无能或者邪恶的程序员编写无法阅读的代码。因此,仅当我们既不称职也不邪恶时,才使用此功能。

回答

我们节省的键入内容,会降低可读性。
许多调试器都不知道我们指的是什么,因此调试更加困难。
它不会使程序运行得更快。

考虑将with语句中的代码作为我们所引用对象的方法。

回答

在工作中,我们给出了从现有的Win 32代码库中删除Withs的要点,因为维护使用它们的代码需要付出额外的精力。我在上一份工作中发现了几个错误,其中一个名为BusinessComponent的局部变量被位于具有相同类型的已发布属性BusinessComponent的对象的With开头块遮盖了。编译器选择使用已发布的属性,以及打算使用局部变量崩溃的代码。

我看过类似的代码

用a,b,c,d来做(除非它们是更长的名字,在这里只是简称)
开始
i:= xyz;
结尾;

试图找到xyz的来源可能是一个真正的痛苦。如果是c,我会尽快将其写为

i:= c.xyz;

我们认为理解这一点很琐碎,但不是一开始就用a开头的800行长的函数!

回答

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

人们不喜欢" with"的主要原因是,它可能会引起名称空间范围和优先级的混淆。

在某些情况下,这是一个实际问题,而在某些情况下这不是问题(非问题案例将在问题中描述为"明智使用")。

由于可能会造成混淆,即使在可能不会出现此类混淆的情况下,某些开发人员也选择完全避免使用" with"。这似乎是教条,但是可以争论的是,随着代码的更改和增长,即使在将代码修改到一定程度以至于使" with"造成混淆的情况下," with"的使用也可能会保留,因此最好不要首先介绍它的用法。

回答

我们最近在Delphi编码标准中禁止了它。

优点经常超过缺点。

那是由于误用而引入的错误。这些并不能证明节省时间来编写或者执行代码。

是的,使用with可以(中等)加快代码执行速度。

在下面的示例中,foo仅被评估一次:

with foo do
begin
  bar := 1;
  bin := x;
  box := 'abc';
end

但是,这里它被评估了三遍:

foo.bar := 1;
foo.bin := x;
foo.box := 'abc';

回答

... run faster ...

通常,编译器/解释器在代码优化方面不一定比我们更好。

我认为这使我说"好极了!"因为当我阅读代码(尤其是其他人的代码)时比较懒惰,所以我喜欢看到显式的代码。因此,我什至会在Java中编写" this.field"而不是" field"。

回答

这主要是维护问题。

从语言的角度来看,WITH的想法具有合理的意义,并且合理地使用它的理由是,保持代码的说法更合理,更小,更清晰。但是,问题在于,大多数商业代码将在其生命周期内由几个不同的人维护,而当编写时,它最初是一个小的,易于解析的构造,但是随着时间的流逝,它很容易转变为笨拙的大型结构,而WITH的范围并非如此维护人员可以轻松解析。这自然会产生错误,并且很难在那找到错误。

例如,说我们有一个小的函数foo,其中包含三到四行代码,这些代码已包装在WITH块中,那么确实没有问题。然而,几年后,在几个程序员的带领下,此功能可能扩展为仍然封装在WITH中的40或者50行代码。现在这很脆弱,并且容易引入错误,尤其是如果维护人员开始引入额外的嵌入式WITH块时,则尤其如此。

WITH没有其他好处,代码应该完全相同地解析并以相同的速度运行(我在D6中对此进行了一些实验,用于3D渲染的紧密循环中,我发现没有区别)。调试器无法处理它也是一个问题,但是应该早就解决,应该忽略是否有任何好处。不幸的是没有。

回答

如果with语句可以通过以下方式扩展,那就太好了:

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

我们无需声明变量" x"。它将由编译器创建。使用哪个函数写起来很快,没有混乱。

回答

我们可以与语句组合,因此最终得到

with Object1, Object2, Object3 do
begin
  //... Confusing statements here
end

而且,如果我们认为调试器与"一"混淆,那么我看不到有人可以确定" with"块中发生了什么。

回答

对于Delphi 2005,在with-do语句中存在硬错误,即评估指针丢失并向上移动指针。必须使用局部变量,而不是直接使用对象类型。