delphi“与”关键字是一个不好的做法?
我一直在阅读有关delphi的关键字坏的事情,但在我看来,如果你不过度使用它。 它可以使你的代码看起来简单。
我经常把所有的TClientDataSets和TFields放在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是包含具有这个代码的方法的types的一个属性。
现在,两年之后,一些善意的开发者进来为TFoo增加了一个Baz属性。 你的代码默默地改变了意思。 编译器不会抱怨,但是代码现在被破坏了。
with关键字是使代码更易读的一个很好的function,但也有一些缺陷。
debugging:
当使用这样的代码:
with TMyClass.Create do try Add('foo'); finally Free; end;
没有办法检查这个类的属性,所以总是声明一个variables,并使用with关键字。
接口:
在with子句中创build一个接口时,直到你的方法结束:
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
我倾向于一味地唠叨那句话。 如前所述,它可以使事情变得复杂,我的经验就是这样。 debugging器很多时候都希望通过withs来评估值,而且我经常发现嵌套的内容会导致难以读取的代码。
Brian的代码似乎可读性好,但是如果你只是直接对发送者进行types转换,那么代码就会变得更短,
TAction(Sender).Enabled := Something;
如果你对打字有很大的担心,我会暂时贬低这个长名的东西:
var t: TTable; begin t := theLongNamedDataModule.WithItsLongNamedTable; t.FieldByName(' '); end;
不过,我不明白为什么打字会打扰你。 我们是打字员第一,程序员第二 ,代码完成,复制粘贴和键盘logging可以帮助你成为一个更有效的打字员。
更新:刚刚偶然发现一篇关于with-statement的小文章: 他用关键字。 语言中最可怕的,危险的,自吹自擂的function。 🙂
当我第一次开始使用pascal编程(使用TurboPascal!)并学习时,WITH看起来非常棒。 正如你所说,对于那些长时间的logging来说,这种繁琐的打字方法是非常理想的。 自delphi到达后,我一直在删除它,并鼓励其他人放弃它 – Verity在registry中整齐地总结除了减less可读性外,还有两个主要原因可以避免:
- 如果你使用一个类,那么你不需要它 – 只有logging“似乎”从中受益。
- 用Ctrl-Enter使用debugging器来跟踪声明的代码是行不通的。
这就是说,为了可读性,我仍然使用语法:
procedure ActionOnUpdate( Sender : TObject ) begin With Sender as TAction do Enabled := Something end;
我还没有看到更好的构造。
在我看来,你点击一个button访问数据模块的例子是一个devise得不好的例子。 如果将这些代码移入数据模块,那么对WITH的全部需求就会消失。 OnClick然后调用LongNameDataModule.InsertStuff并且不需要。
With是一个糟糕的设备,你应该看看你的代码,看看你为什么需要它。 你可能做错了什么,或者可以做更好的方法。
正如Vegar所提到的那样,它就像一个整洁,可读性更强,更容易debugging,不太容易出现隐藏问题的临时引用。
到目前为止,我从来没有发现需要使用。 我曾经对此感到矛盾,直到我接手一个使用双倍频率的心灵弯曲的项目。 质疑原始开发者是否打算引用第一个或第二个中的项目,如果这个模棱两可的引用是有缺陷或笨拙的代码,试图debugging它的折磨,以及扩展或修改使用这些可憎的事情不值得任何人的时间。
显式代码更简单易读。 这样,你可以吃你的蛋糕,享受它吃。
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;
与“与”的主要问题是,你不知道它的范围结束,你可以有多个与语句重叠。
我不认为你应该避免使用它,只要你的代码是可读的。
其中一个提高可读性的build议是,如果codegear增加了允许使用别名的选项 ,并且可能允许多个withs在一个中:
procedure TMyForm.AddButtonClick(Sender: TObject); begin with LongNameDataModule as dm, dm.LongNameTable1 as t1, dm.LongNameTable2 as t2 do begin t1.Insert; t1.FieldByName('Field1').AsString := 'some value'; t1.FieldByName('Field2').AsString := t2.FieldByName('Field2').AsString; t1.Post; dm.Connection.Commit; end end;
就我而言,在你给予的情况下,With是完全可以接受的。 这当然会提高代码的清晰度。
真正的罪恶是当你有多个打开一次。
另外,我的意见是,你使用的是什么使得很大的不同。 如果这是一个真正不同的对象,那么这可能是一个坏主意。 然而,我不喜欢在一个层次上有很多variables,即使这是有意义的 – 通常是包含整个非常复杂的数据项目的数据对象 – 通常是程序devise用来处理的整个工作。 (我不认为这种情况会发生在没有这样的项目的应用程序中。)为了让世界更清晰,我经常使用logging来分组相关项目。 我发现,几乎所有我用的是用于访问这样的子组。
我坚信在Delphi中移除WITH支持。 使用具有命名字段的datamodule的示例用法是我可以看到的唯一实例。 否则,最好的反对意见是由克雷格·斯图兹(Craig Stuntz)给出的,我投了票。
我只想指出,随着时间的推移,你可能最终(应)在OnClick事件中丢弃所有的代码,并且你的代码也将最终从数据模块上的命名字段迁移到使用包装这些数据的类,使用WITH的原因将消失。
你的问题是一个很好的例子,“锤子并不总是解决scheme”。
在这种情况下,'with'不是你的解决scheme:你应该将这个业务逻辑从你的表单移到你的datamodule。 这样做违反德米特法律像迈克尔 (迈克尔Hieke)已经评论。
也许你的例子只是说明,但如果你实际上在你的项目中使用这样的代码,这是你应该做的:
procedure TLongNameDataModule.AddToLongNameTable1(const NewField1Value: string); begin LongNameTable1.Insert; LongNameTable1_Field1.Value := NewField1Value; LongNameTable1_Field2.Value := LongNameTable2_LongNameField1.Value; LongNameTable1_Field3.Value := LongNameTable3_LongNameField1.Value; LongNameTable1_Field4.Value := LongNameTable4_LongNameField1.Value; LongNameTable1.Post; end;
然后从你的表格中这样调用它:
procedure TMyForm.AddButtonClick(Sender: TObject); begin LongNameDataModule.AddToLongNameTable1('some value'); end;
这有效地摆脱了你的声明,同时使你的代码更易于维护。
当然,用单引号括起来的Delphistring也可以帮助编译它们;-)
这里有很多优秀的答案,为什么with声明是坏的,所以我会尽量不要重复。 多年来我一直在使用with语句,而且我已经开始避开它了。 这部分是因为可能很难计算范围,但是最近我开始重构,没有一个自动化的重构使用with语句 – 自动重构是非常棒的。
还有一段时间以前,我做了一个关于为什么with声明不好的video,这不是我最好的作品之一,但在这里
目前的声明是“危险的”,但可以大大改善:
With TForm1.Create (Nil) Do // New TForm1 instance Try LogForm ("); // That same instance as parameter to an outer method "ShowModal; // Instance.ShowModal Finally "Free; // Instance.Free End;
我的build议是:
- 每个With标题不超过一个对象/logging。
- 不允许嵌套Withs。
- “用法”来表示对象/logging(双引号类似于同上标记: http : //en.wikipedia.org/wiki/Ditto_mark )。