我一直在阅读关于delphi中with关键字的坏事,但在我看来,如果你不过度使用它.它可以使您的代码看起来很简单.
我经常将所有TClientDataSets和TField都放在TDataModules中.所以在我的表单中我有这样的代码
procedure TMyForm.AddButtonClick(Sender: TObject); begin with LongNameDataModule do begin LongNameTable1.Insert; LongNameTable1_Field1.Value := "some value"; LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value; LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value; LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value; LongNameTable1.Post; end end;
如果没有with关键字,我必须编写这样的代码
procedure TMyForm.AddButtonClick(Sender: TObject); begin LongNameDataModule.LongNameTable1.Insert; LongNameDataModule.LongNameTable1_LongNameField1.Value := "some value"; LongNameDataModule.LongNameTable1_LongNameField2.Value := LongNameDataModule.LongNameTable2_LongNameField1.Value; LongNameDataModule.LongNameTable1_LongNameField3.Value := LongNameDataModule.LongNameTable3_LongNameField1.Value; LongNameDataModule.LongNameTable1_LongNameField4.Value := LongNameDataModule.LongNameTable4_LongNameField1.Value; LongNameDataModule.LongNameTable1.Post; end;
我认为使用with关键字更容易阅读.
我应该避免使用with关键字吗?
除了"A,B,C,D"这样的病态条件之外,最大的危险就是你的代码可以默默地改变意义,而不会给你注意.考虑这个例子:
with TFoo.Create try Bar := Baz; DoSomething(); finally Free; end;
你知道Bar是TFoo的属性,你编写这段代码,而Baz是包含具有此代码的方法的类型的属性.
现在,两年后,一些善意的开发人员为TFoo添加了一个Baz属性.你的代码默默地改变了意义.编译器不会抱怨,但代码现在已经破坏了.
在与关键字是使代码更易读一个不错的功能,但也有一些缺陷.
调试:
使用这样的代码时:
with TMyClass.Create do try Add('foo'); finally Free; end;
无法检查此类的属性,因此始终声明一个变量并在其上使用with关键字.
接口:
在with子句中创建接口时,它会一直存在到方法的末尾:
procedure MemoryHog; begin with GetInterfaceThatTakes50MBOfMemory do Whatever; ShowMessage('I''m still using 50MB of memory!'); end;
明晰
在with子句中使用具有属性或方法名称的类时,它可能很容易欺骗您.
with TMyForm.Create do Width := Width + 2; //which width in this with is width?
当然,当具有重复的名称时,您将使用在with语句(TMyForm)中声明的类的属性和方法.
该with
声明有它的地方,但我不得不承认,过度使用会导致模糊的代码.一个好的经验法则是在添加with语句后确保代码"更"可读和可维护.如果您觉得在添加语句后需要添加注释来解释代码,那么这可能是一个坏主意.如果代码在您的示例中更具可读性,则使用它.
顺便说一句:这总是我最喜欢的Delphi中用于显示模态窗口的模式之一
with TForm.Create(nil) do try ShowModal; finally Free; end
我倾向于完全否定声明.如前所述,它可以使事情变得复杂,我的经验是它会.很多时候,调试器需要因为withs而评估值,并且我经常发现嵌套的内容会导致难以阅读的代码.
Brian的代码看起来既可读也不错,但如果您直接对发件人进行类型转换,代码会更短,并且您删除了对启用该组件的所有疑问:
TAction(Sender).Enabled := Something;
如果你担心打字很多,我会为这个长命名的对象做一个临时的报复:
var t: TTable; begin t := theLongNamedDataModule.WithItsLongNamedTable; t.FieldByName(' '); end;
我不能理解为什么打字应该打扰你.我们是打字员第一,程序员第二,代码完成,复制粘贴和按键录制可以帮助您成为更有效的打字员.
更新:偶然发现了一篇长篇文章,其中包含一些关于with语句的部分:他带有关键字.语言中最丑陋,最危险,最自信的特点.:-)
当我第一次开始pascal编程(使用TurboPascal!)并且在我去的时候学习,WITH看起来很精彩.正如你所说,单调乏味的答案和理想的长记录.自Delphi到来以来,我一直在删除它并鼓励其他人放弃它 - 由Verity在寄存器中巧妙地总结 除了可读性的降低之外,我有两个主要原因可以避免它:
如果你使用一个类,那么你无论如何都不需要它 - 只有记录'似乎'才能从中受益.
使用调试器通过Ctrl-Enter跟踪代码到声明不起作用.
也就是说,为了便于阅读,我仍然使用以下语法:
procedure ActionOnUpdate( Sender : TObject ) begin With Sender as TAction do Enabled := Something end;
我没见过更好的结构.
你的例子,按钮点击中的数据模块访问,在我看来是一个设计糟糕的例子.如果将此代码移动到应该存在的数据模块中,则对WITH的全部需求消失了.OnClick然后只调用LongNameDataModule.InsertStuff并且没有需要.
使用是一个糟糕的设备,你应该看看你的代码,看看你需要它的原因.你可能做错了什么,或者可以做得更好.
正如Vegar所提到的那样,它的整洁性和可读性更高,调试更容易,并且使用临时引用时不太容易出现隐形问题.
到目前为止,我还从来没有发现需要使用带.我曾经对此感到矛盾,直到我接管了一个经常使用思维弯曲双项目的项目.质疑意原开发商是否引用的项目在第一与和第二,如果不明确的引用是有滑或笨拙的代码,试图调试它的折磨,并在延长或修改使用这些类的效果敲可憎的是不值得任何人的时间.
的显式代码是简单地更具有可读性.这样你就可以吃蛋糕,享受美食.
procedure TMyForm.AddButtonClick(Sender: TObject); var dm: TLongNameDataModuleType begin dm:=LongNameDataModule; dm.LongNameTable1.Insert; dm.LongNameTable1_Field1.Value := "some value"; dm.LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value; dm.LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value; dm.LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value; dm.LongNameTable1.Post; end;
我坚信在Delphi中删除WITH支持.使用带有命名字段的数据模块的示例用法是我能看到它的唯一实例.否则,反对它的最好的论据是克雷格·斯特恩茨(Craig Stuntz)给出的 - 我投票了.
我只想指出,随着时间的推移,你最终可能(应该)在OnClick事件中删除所有编码,你的代码最终也会从数据模块的命名字段迁移到使用包装这些数据的类,并且使用WITH的原因将会消失.