Delphi“with”关键字是一种不好的做法吗?

时间:2009-02-05 03:53:46

标签: delphi with-statement

我一直在阅读关于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 关键字更容易阅读。

我应该避免使用关键字吗?

14 个答案:

答案 0 :(得分:58)

除了“A,B,C,D”这样的病态条件之外,最大的危险在于你的代码可以在不通知你的情况下默默地改变意义。考虑这个例子:

with TFoo.Create
try
  Bar := Baz;
  DoSomething();
finally
  Free;
end;

您编写此代码时知道Bar是TFoo的属性,而Baz是包含具有此代码的方法的类型的属性。

现在,两年后,一些善意的开发人员为TFoo添加了一个Baz属性。你的代码默默地改变了意义。编译器不会抱怨,但代码现在已经破坏了。

答案 1 :(得分:31)

with 关键字是一个很好的功能,可以让您的代码更具可读性,但存在一些缺陷。

<强> 调试:

使用这样的代码时:

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)中声明的类的属性和方法。

答案 2 :(得分:22)

with声明有它的位置,但我必须同意过度使用会导致模糊的代码。一个好的经验法则是在添加with语句后确保代码“更”可读和可维护。如果您觉得在添加语句后需要添加注释来解释代码,那么这可能是一个坏主意。如果代码在您的示例中更具可读性,则使用它。

btw:这总是我最喜欢的Delphi模式之一,用于显示模态窗口

with TForm.Create(nil) do
try
  ShowModal;
finally
  Free;
end

答案 3 :(得分:15)

我倾向于完全禁止声明。如前所述,它可以使事情变得复杂,我的经验是它会。很多时候,调试器需要因为withs而评估值,并且我经常发现嵌套的内容会导致难以阅读的代码。

Brian的代码似乎可读且不错,但如果您直接对发件人进行类型转换,代码会更短,并且您删除了对启用该组件的所有疑问:

TAction(Sender).Enabled := Something;

如果你担心打字很多,我会为这个长期命名的对象做出临时的反对:

var
  t: TTable;
begin
  t := theLongNamedDataModule.WithItsLongNamedTable;
  t.FieldByName(' ');
end;

我不能理解为什么打字应该打扰你。 We Are Typists First, Programmers Second,代码完成,复制粘贴和密钥记录可以帮助您成为更有效的打字员。

更新: 只是偶然发现了一篇长篇文章,其中有一些关于with-statements的部分:he with keyword. The most hideous, dangerous, blow-your-own-feet-off feature in the language.: - )

答案 4 :(得分:8)

当我第一次开始pascal编程(使用TurboPascal!)并且在我去的时候学习,WITH看起来很精彩。正如你所说,单调乏味的答案和理想的长记录。自德尔福到达以来,我一直在删除它,并鼓励其他人放弃它 - 由Verity在the register整齐地总结出来 除了降低可读性之外,我还有两个主要原因可以避免它:

  1. 如果你使用课程,那么你无论如何也不需要它 - 只有“似乎”的记录才能从中受益。
  2. 使用调试器按照Ctrl-Enter跟踪声明的代码不起作用。
  3. 那就是说,为了便于阅读,我仍然使用语法:

    procedure ActionOnUpdate( Sender : TObject )
    begin
      With Sender as TAction do
        Enabled := Something
    end;
    

    我没有看到更好的构造。

答案 5 :(得分:6)

在我看来,你在一个按钮点击中访问数据模块的例子是一个设计不当的例子。如果将此代码移动到应该存在的数据模块中,则对WITH的全部需求消失了。 OnClick然后只调用LongNameDataModule.InsertStuff,并且没有需要。

使用是一个糟糕的设备,你应该看看你的代码,看看你需要它的原因。你可能做错了什么,或者做得更好。

答案 6 :(得分:6)

正如Vegar所提到的那样,它的整洁性和可读性更高,调试更容易,并且使用临时参考时不太容易出现隐形问题。

到目前为止,我从未发现需要使用。我曾经对此感到矛盾,直到我接手一个项目,经常使用双重。质疑原始开发人员是否打算引用第一个中的项目或第二个,如果该模糊引用是 with-slip 或笨拙的代码,那么试图调试它的痛苦,以及扩大或修改使用这些可憎之类的类别的效果并不值得任何人花时间。

显式代码简单更具可读性。通过这种方式,你可以吃蛋糕并享用它。

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;

答案 7 :(得分:5)

我坚信在Delphi中删除WITH支持。使用带有命名字段的数据模块的示例用法是我能看到它的唯一实例。否则,反对它的最好的论据是由克雷格·斯特恩茨给出的 - 我投票了。

我只想指出,随着时间的推移,您最终可能(应该)在OnClick事件中使用所有编码,并且您的代码最终也会从数据模块上的命名字段迁移到使用包装此数据的类以及使用WITH的原因会消失。

答案 8 :(得分:4)

你的问题是“锤子并不总是解决方案”的一个很好的例子。

在这种情况下,'with'不是您的解决方案:您应该将此业务逻辑从表单中移出到您的数据模块中。 不这样做会违反Law of Demeter mghie(Michael 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;

这有效地消除了你的with语句,并使你的代码同时更易于维护。

当然,使用单引号包围Delphi字符串也有助于编译它; - )

答案 9 :(得分:3)

“with”的主要问题是你不知道其范围的结束位置,并且你可能会多次与语句重叠。

我不认为你应该避免使用它,只要你的代码是可读的。

使其更具可读性(并且在更长的代码中更少混淆)的提议之一是,如果codegear添加了选项以允许使用别名,并且可能允许多个with在一个:

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;

答案 10 :(得分:3)

就我而言,在你给予的情况下,这是完全可以接受的。它肯定会提高代码的清晰度。

真正的邪恶是当你有多个同时打开时。

另外,我的观点是你使用的内容会带来很大的不同。如果它是一个真正不同的对象,那么with可能是一个坏主意。但是,我不喜欢在一个级别上有很多变量,即使这是有意义的 - 通常是包含整个非常复杂的数据项的数据对象 - 通常是程序设计用于处理的整个工作。 (我不认为这种情况会发生在没有这样一个项目的应用程序中。)为了让世界变得更清晰,我经常使用记录对相关项目进行分组。我发现我使用的几乎所有内容都是为了访问这些子组。

答案 11 :(得分:2)

这里有许多优秀的答案,为什么with语句是坏的,所以我会尽量不重复它们。多年来我一直在使用with语句,而且我已经开始回避它了。这部分原因可能很难找出范围,但我最近开始进行重构,并且没有一个自动重构与with语句一起工作 - 并且自动重构很棒。

前段时间我制作了一段视频,说明为什么声明不好,这不是我最好的作品之一,Here it is

答案 12 :(得分:0)

仅暂时使用with(就像您暂时注释掉一样)。

它可以帮助您编写代码草图,以便快速编译和运行。如果您整合解决方案,请将其清理干净!将代码移动到正确位置时删除with

答案 13 :(得分:-1)

当前的With声明是&#34;危险&#34;,但它可以大大改善:

  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;

我的建议是:

  1. 每个With标题不得超过一个对象/记录。
  2. 不允许使用嵌套。
  3. 使用&#34;表示对象/记录(双引号类似  同上标记:http://en.wikipedia.org/wiki/Ditto_mark)。