Delphi中在uses子句中使用条件编译

11

我想修改我的Delphi 2010代码以便在XE7中编译(并且希望仍能在2010中编译)。因此,在包含主窗体的单元中,我添加了条件指令。以下代码在2010中可以正常工作。

uses 
  {$IF CompilerVersion >= 24}System.Actions, {$ELSE}Actnlist,{$IFEND}
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,  Dialogs;

但是XE7自动在结尾添加了System.Actions,这样uses从句中就有两次声明了System.Actions(如下所示),并给出错误信息[dcc32 Error] MyForm.pas(10): E2004 Identifier redeclared: 'System.Actions'。为什么XE7不接受条件指令内的单元?

uses 
  {$IF CompilerVersion >= 24}System.Actions, {$ELSE}Actnlist,{$IFEND}
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,  Dialogs,
  System.Actions; // <- automatically added

4
因为表单相关单元中使用的接口使用子句属于IDE本身,并且它会为放置在表单上的任何组件添加当前版本所需的内容。克服这个问题的唯一方法是从表单中删除组件,将声明放置在private部分而不是默认的published部分,并在运行时创建组件。你正在与IDE本身作斗争,你不会赢得这场战斗。 - Ken White
但我已经拥有所有需要的单元了,不是吗?Delphi正在处理{$IF CompilerVersion >= 24}System.Actions{$IFEND}部分。如果我犯了一个“笔误”,比如({$IF CompilerVersion >= 24}System.Action*s{$IFEND}),它会抱怨。因此,它知道接口使用中存在System.Actions。难道IDE和编译器不同步吗? - ss2006
2
IDE不知道条件定义。它只知道您在表单上有某个组件,并且在uses子句中找不到该单元,因此会添加它。正如我所说,接口uses子句属于IDE,而不是您。如果组件在表单上,则IDE将确保该单元直接包含在uses子句中。为了明确起见,我将重复第三次:与表单相关的单元中的interface uses子句属于IDE,涉及到放置在表单上的任何组件。 - Ken White
1
好的。你提出了问题,我提供了信息。我不会和你辩论,但自从Delphi 2发布以来,我就一直在处理这个问题,并且需要支持在16位和32位版本组件之间进行条件定义。 :-) 你正在与IDE进行一场失败的战斗 - 它总是获胜。 - Ken White
1
顺便说一句,我认为你在这里缺失的部分是IDE不会编译代码来确定组件使用子句中有什么(或没有什么)。它只是解析使用子句以查看是否具有表单上所需组件的所有必需单元,这就是为什么它无法看到包含在条件语句中的内容。(条件语句是为了编译器,而IDE不会为此目的进行编译。) - Ken White
显示剩余2条评论
4个回答

14

正如Ken所说,界面使用子句将由IDE进行修改,这是通过某些不太复杂的过程实现的(正如您已经发现的那样)。同样的问题也会影响项目使用子句。不幸的是,在Form/DataModule使用子句的情况下,避免这种情况要困难得多。

您可以使用单元别名(请参见David Heffernan的答案),但需要注意,如果为IDE想要添加的单元创建别名,则IDE仍将添加对所需单元的引用,因为它不认识别名作为标识该所需单元。将别名设为System单元将避免这种情况,因为每个单元已经(隐式地)使用了它。

另一种选择是从使用列表中删除所有这种条件,并根据需要创建占位符单元,以便您希望在项目上使用的每个编译器都可以通过来自每个IDE坚持所需的列表合并的单个使用列表得到满足(IDE不会从使用列表中删除未使用的单元,这常常是一个抱怨,但在这种情况下实际上有助于解决您的问题)。

在这种情况下,在Delphi 2010项目中创建一个空的Actions单元:

 unit Actions;
 interface
 implementation
 end.

当然,您需要确保该单元不在X7版本项目的项目路径中。

实现这一点的一种方法是确保空的Actions.pas单元没有显式列在DPR使用列表中,但是放置在您项目源代码的子文件夹(例如“placeholders”)中。然后,您可以将此子文件夹添加到Delphi 2010版本的项目搜索路径中,但不添加到XE7版本中:

 \Project Folder

     project2010.dpr
     project2010.dproj
     projectXE7.dpr
     projectXE7.dproj

     \placeholders
          Actions.pas

如果您发现每个不同版本需要占位符,则需要单独的占位符文件夹。您可以创建进一步的版本特定子文件夹,例如:

     \placeholders
          \2010
               Actions.pas
          \XE7
               D2010UnitNotPresentInXE7.pas

仅从创建一个自动/自我记录组织的角度来看,这种结构可能是明智的。

请注意,只有在处理表单(或框架等)的接口部分中的使用子句中的单元引用时才需要这样做。 在非可视单元或实现部分中,IDE 不会干扰,因此条件编译指令在那里不应该出现任何问题。


2
我有点困惑 - @Deltics似乎在说别名不起作用 - “你甚至不能使用单元别名”,但David说可以。Deltics,我理解你的意思了吗? - Hugh Jones
我已更新答案,以明确区分并将读者重定向到David的答案,因为这比使用占位符单位要简单得多。 :) - Deltics
你可以使用占位符单元做的另一件事是添加 {$IF CompilerVersion >= 24} {$Message Error '不要在新版本的 Delphi 中包含此单元'} {$IFEND},以便在不小心将其包含在更高版本的项目中时强制编译器出错。 - Disillusioned
@Craig,这样做有点违背了拥有一个单一的源代码单元的初衷,该单元可以在所有版本的Delphi中编译(避免不同的IDE版本混淆表单接口使用子句)。话虽如此,发出提示或警告可能会很有用,而不是错误。但是使用更优雅的“别名到系统单元”的方法使得这一点无关紧要。 :) - Deltics
我现在明白你的意思了,但我们所讨论的是由IDE添加到表单使用子句中的单位,而不是显式地添加到表单或DPR使用列表中。因此,虽然您永远不应向较新版本的项目DPR添加占位符单位,但在任何项目DPR使用中都不应添加占位符或其他VCL单位,除非您已经特别更新/替换了VCL单位(例如修复原始VCL单位中的错误),在这种情况下,您需要解决更大的问题来隔离特定于版本的VCL替换单位。 - Deltics
显示剩余7条评论

9
最简单的解决方法是在你的Delphi 2010项目中添加一个单元别名。你需要为不同的Delphi版本使用不同的.dproj文件,但无论如何都需要这样做。
在Delphi 2010项目的单元别名设置中添加以下内容:
Actions=System

我将“System”用作别名目标,因为“System”单元被自动包含在每个Delphi单元中,所以别名包含是无害的。这是我能想到的最简单的方法,可以使编译器有效地忽略uses子句中的一个条目。
然后你可以像这样声明你的uses子句:
uses 
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, 
  Actions, Actnlist;

在Delphi 2010中,这段代码可以编译通过,因为别名处理会将Actions映射到System。在XE7中同样也可以,因为没有别名,在IDE中存在Actions单元库,所以不需要修改uses子句。


根据提问者的问题,Delphi XE7没有Actnlist单元,因此不清楚为什么XE7 IDE会对在uses子句中存在缺失单元感到满意。 - kludg
@user246408 XE7肯定有Actnlist:http://docwiki.embarcadero.com/Libraries/XE7/en/Vcl.ActnList - David Heffernan
好的,那么你和Deltic的回答略微回答了不同的问题。 - kludg
@user246408 我不这么认为。用户可以随心所欲地使用ActnList或不使用。问题在于如何应对XE7 IDE强制将Actions加入到使用子句中的情况。这是我自己遇到的问题。Deltics提供了另一种方法来排列旧版本,其VCL没有Actions单元,以接受使用子句中存在Actions单元的情况。我认为你误解了某些事情。 - David Heffernan

1
我们遇到了相同的问题... 最简单的方法是这样做:
{$IF CompilerVersion < 24}{$ELSE}System.Actions,{$IFEND}
{$IF CompilerVersion >= 24}{$ELSE}Actnlist,{$IFEND}

如果您在旧的IDE中打开文件,则可能会看到一个错误,提示“单元X”未找到,但它可以编译成功,并且不进行自动添加。这看起来不太好,但它运行得非常好...
此致敬礼,
Bernd

1

会有什么问题吗?

(保留HTML标签)
{$IF CompilerVersion < 24}Actnlist,{$IFEND}

还是这个一个学术论点?

附加说明...

然后在你的2010编译路径中添加一个虚拟的System.Actions.dcu文件,其中什么都没有。

我猜测IDE会坚持插入uses ... System.Actions,2010会得到它想要的,XE7会有它想要的。

但是我没有XE7,所以无法测试它。


是的。 :-) 这个问题完全相同 - 它不会在与设计时放置的组件相关的单元接口使用子句中起作用。IDE 拥有该使用子句,任何与它的争斗都是徒劳的。 (请阅读对原始问题发布的评论。)颠倒条件(以及受影响的单位)也不会改变这个事实。 - Ken White

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