为什么我不应该在Delphi中使用" with"?
我听说很多程序员,尤其是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语句中存在硬错误,即评估指针丢失并向上移动指针。必须使用局部变量,而不是直接使用对象类型。