Delphi 2006中“WITH”语句的调试问题

6

可能是重复问题:
Delphi中的“with”有什么问题

我在BDS 2006中调试使用‘WITH’语句的代码时遇到了问题,调试器无法显示类或记录中变量的值。我做错了什么还是BDS 2006存在错误?

type
  TNumber = class
      Num: Integer;
  end;

implementation

{$R *.dfm}

var
   MyNumber: TNumber;

procedure TForm2.FormCreate(Sender: TObject);
begin
   MyNumber := TNumber.Create;
   MyNumber.Num := 10;   /// MyNumber.Num Can be seen with debugger
   with  MyNumber do
   begin
     Num := Num +1 ;           /// Num is not seen by the  debugger
     MyNumber.Num := Num +1 ;  /// MyNumber.Num is seen but Num is not seen by the  debugger
   end;
end;

编辑:

当然可以使用变量的完整名称,但是如果您有一个复杂的结构,具有多个级别,事情会变得非常混乱。


请参见https://dev59.com/d3VD5IYBdhLWcg3wJIEK。 - Lars Truijens
8个回答

17
With 被许多人认为是属于“只要你拥有它,不代表你必须使用它”类别的语言特性。在极其复杂的多级结构中使用时,编译器没有按照您的预期执行而使用它会更加容易,但这种情况非常少见,我只发现了一两个这样的情况,而在10年的Delphi编程中,我认为这些情况可以用一只手数出。
在实际应用中,虽然代码看起来更整洁,但在检查您没有编写或长时间未使用的代码时,弄清楚变量是简单变量还是结构变量的维护成本很高。此外,我使用过的每个Delphi版本都存在调试器方面的已知问题,这是关键因素。

3
我希望看到有必要使用 WITH 子句的例子。我也用 Delphi 做了十年,其中 9.5 年都在删除 WITH 子句并按照 Fabricio 的回答替换别名。 - Ben Laan
据我回忆,它是在D6上,具有类似多级结构的某些内容,其中一个子节点是指向另一个结构的指针。如果没有With子句,似乎不可能部署任何访问第二个结构中项目的语法。 - Cruachan
啊,别名...我在写我的答案时一直在努力回忆那个词...;-) - Fabricio Araujo

8

当您将鼠标悬停在变量上时,调试器无法连接要检查的源代码中显示的变量与相关的with语句。 您需要在调试监视窗口中检查值并指定完整的变量,例如MyNumber.Num。


2
我认为“不能”太宽容了。我会说它的设计并没有达到预期的效果。 - Argalatyr

6
with语句是一种类似于语法糖的语法,但它留下了不良影响,长期使用会产生实际危害。最好不要使用它。

1
ROFL!!!!!哈哈哈哈哈哈哈!!!太棒了! - Fabricio Araujo

6

使用 with 语句时,调试问题是已知的。这就是我看到它们时将其删除的原因之一。在我看来,你不应该以可维护性为代价提高编码速度。

我经常看到的一个常见结构 (并且学会了厌恶) 是:

with TMyObject.Create do
  try
    Method1(blah, blah, blah);
    Method2(blah, blah, blah);
  finally 
    Free;
  end;

在with语句中,甚至可以添加更多的结构:

with A, B, C, D do
  // Aargh!

然而,如果您可以替换以下代码,则有时使用with语句是有效的:

A.B.C.D.E.Method1;
A.B.C.D.E.Method2;
A.B.C.D.E.Method3;
A.B.C.D.E.Method4;
A.B.C.D.E.Method5;
A.B.C.D.E.Method6;

随着

with A.B.C.D.E do begin
  Method1;
  Method2;
  Method3;
  Method4;
  Method5;
  Method6;
end;

虽然使用A.B.C.D.E有些值得质疑,但它往往是“delphi方式”。 但现在通过类辅助器,我们可以拥有一个完全有效的解决方案:

TAHelper = class helper for TA
public
  procedure Method1;
endl

procedure TAHelper.Method1;
begin
  // You can (should) add sanity checks here.
  B.C.D.E.Method1;
end;

现在您可以使用:

A.Method1;

在我看来,这要好得多。

我不太喜欢类助手... 只有在匆忙且没有时间进行更清晰但更长的解决方案(这意味着编辑许多文件,改变类等)时才会使用它们。 类助手使代码更难以理解。 - Fabricio Araujo

6

我很少使用With..do,而倾向于像这样做:

var
  Sc : TE_Type;
begin
  Sc := A.B.C.D.E;
  sc.Method1;
  sc.Method2;
  sc.Method3;
  sc.Method4;
  sc.Method5;
  sc.Method6;
end;

当然,一个像样的名称对Sc来说并不会有害。我认为这比使用with..do更清晰,调试器也能更好地找到它自己。因此,我很喜欢那些“快捷方式”变量。


确认。在大多数情况下,只要我们没有别名的“with”或“using”,这是最好的解决方案。在我看来。 - Uli Gerhardt

4

“With”引起了歧义,可能会引起更多的问题而不是解决问题。考虑不使用它。

Castalia有一个重构功能可以帮助消除它们。


2
我完全相信“with”子句的恐惧只是为了让书籍作者不必在示例代码中使用所有那些丑陋的TWinComponent字符串。 在现实生活中,除了代码片段和教科书之外,几乎没有什么好理由使用“with”。
主要原因是它会破坏调试器。当查找变量的值时,调试器评估整个子句是非常不切实际的,所以这不是一个错误,而是我所知道的所有Delphi调试器都不支持它。 如果你像我一样被困在维护数十万行代码的泥潭中,这些代码基本上是从教科书上复制出来的,那么调试就会变成一场噩梦。
底线是,永远不要使用with子句!

主要原因是,如果你不非常小心使用它,它会导致作用域解析问题。破坏调试器只是一个次要的麻烦,它加剧了这个问题。 - Mason Wheeler

-1

With并不是一个坏的实践。唯一的问题是你不能轻易地调试这些代码行,但如果你小心使用,就没有任何问题。也许对于初学者来说不是一个好的实践,但如果你知道在哪里使用它,那就没问题了。

让EMBA尽可能改进它,以避免常见的抱怨。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接