2010-02-01 88 views
5

我偶然在这页Why shouldn’t I call Application​.CreateForm。 现在我有一些像这样的代码:如何避免两次调用Application .CreateForm?

SplashForm := TSplashForm.Create(Application); 
SplashForm.Show; 
SplashForm.Update; // force update 
Application.Initialize; 
Application.CreateForm(TClientData, ClientData); 
SplashForm.Update; // force update 
Application.CreateForm(TClientMainForm, ClientMainForm); 
Application.ShowHint := True; 

Application.Run; 
ClientMainForm.ServerConnected := false; 
FreeAndNil(ClientMainForm); 
FreeAndNil(ClientData); 

首先创建一个splashform,然后一个数据模块和持续的主要形式。该页面说Application.CreateForm不应该被调用两次。上面的代码应该改变吗?

Regards

回答

5

多次使用Application.CreateForm没有任何问题。但是这为每个可能是代码味道的表单引入了全局变量。 不幸的是,IDE为每个表单创建一个。虽然你可以删除它们,如果你喜欢。

更好的方法是在需要时创建表单,并在您准备好时释放它。所以你只使用Application.CreateForm作为主窗体。

主要的数据模块可以通过主窗体创建。但它也可能是全球性的,仅仅是品味的问题。

所以要回答这个问题,可以通过在本地创建和释放表单来避免Application.CreateForm。

该文章提到了Application.CreateForm的副作用(第一个完成的表单是主窗体)。 因此,如果主窗体使用Application.CreateForm创建其他窗体,可能会出现意想不到的副作用。

所以,为了避免任何恶心,你应该限制你的唯一的呼叫。这只用一个全局表单来完成。

+0

我不认为这是更好的风格去除IDE中创建全局。这些是Delphi应用程序工作的一部分。实际上,这种“优化”更像是一种“代码异味”,而不是非优化的IDE生成的代码。 – 2010-02-01 15:21:59

+4

@Warren:这个评论根本没有任何意义。除了主窗体变量以外,Delphi应用程序不需要使用这些全局变量中的*任何*,并且主窗体中的一个可以很容易地被项目文件中的一个变量所替代,这个变量不会被知道任何其他单位,所以它也不会是一个真正的全球变量。 – mghie 2010-02-01 17:38:59

+0

我只是在评论“虽然你可以删除它们,如果你喜欢”这句话,我不认为这是一个好主意。 – 2010-02-01 19:38:24

1

如果TClientData是一个数据模块,并且TClientMainForm是一个表单,那么no(除了可能在最后的两个FreeAndNil调用 - 不是真的需要)。但保重。因为它罗布·肯尼迪说,在他的帖子中,Application.CreateForm确实落后于其他东西(它设置的MainForm变量),所以我会建议建立按以下规则项目文件:

  1. 使用单一调用创建所有想要在启动时创建的表单至Application.CreateForm - 通常这由IDE完成。

  2. 从项目文件中删除您要在程序中动态创建(按需)的表单。 (在Project |选项|表格...) - 从“自动创建窗体”将它们移到“可用”

  3. 创建窗体中使用TmyForm.Create(所有者)你代码(等)和不是Application.CreateForm(...)。顺便说一句,如果你确信你会释放表格,那么最好(为了加快速度)拨打TmyForm.Create( - 低于没有任何所有者。

  4. 如果你想在启动时做一些初始化的,你可以在绑已创建的窗体/数据模块的项目文件中的程序/方法和之前运行的应用程序运行它。

例如:

begin 
    Application.Initialize; 
    Application.MainFormOnTaskbar := True; 
    Application.CreateForm(TdmoMain, dmoMain); //<--this is a data module 
    Application.CreateForm(TfrmMain, frmMain); //<--this will became the main form 
    Application.CreateForm(TfrmAbout, frmAbout); 
    //... other forms created here... 
    frmMain.InitEngine; //<--initialization code. You can put somewhere else, according with your app architecture 
    Application.Run; 
end. 

通过这种方式,您将有项目文件干净,你会确切地知道哪个是哪个。

HTH

+0

+1 PLAINTH的答案解释并遵循了大多数Delphi开发人员使用的既定约定。除此之外的任何事情都违反了我所说的“最小惊奇原则”。 – 2010-02-01 15:23:39

1

当我写这篇文章,我想主要的代码的DPR文件。人们可以在DPR文件中看到由IDE生成的表单创建代码,并认为这是创建表单的最佳方式,所以他们在程序中的其他地方使用。他们有时在主窗体的OnCreate事件处理程序中使用它来创建其程序所需的其他窗体,然后他们遇到了问题,因为该程序的主窗体并非他们的想法。

在您提供的代码中,只需调用一次CreateForm就很容易。用它作为主窗体,除此之外别无它用。数据模块不是主要的形式,所以你不需要CreateForm的魔力。

SplashForm := TSplashForm.Create(Application); 
SplashForm.Show; 
SplashForm.Update; // force update 
Application.Initialize; 

// Change to this. 
ClientData := TClientData.Create(Application); 

SplashForm.Update; // force update 
Application.CreateForm(TClientMainForm, ClientMainForm); 
Application.ShowHint := True; 

Application.Run; 
ClientMainForm.ServerConnected := false; 

// Remove these. 
FreeAndNil(ClientMainForm); 
FreeAndNil(ClientData); 

你真的不应该释放你在这里创建的对象,因为你不拥有它们。它们由全局应用程序对象拥有,因此让它解决它们的问题:删除两个对FreeAndNil的调用。

+0

如果你知道主窗体已经创建好了,那么另一个窗体就不能奇迹般地变成主窗体。如果这个警告被理解了,那么从当前位置以外的地方调用Application.CreateForm是个不错的主意,当且仅当您打算让Application对象从此时开始管理表单的生存期。然而,正如我们许多人希望只要应用程序存在并随后被释放的形式一样,这就是调用Application.CreateForm所需的行为。我称之为“迟到创作”,我一直都在使用它。 – 2010-02-01 19:40:12

+1

主窗体在*指定为主窗体之前可以运行它自己的代码。这就是问题发生的地方。如果你希望Application对象管理生命期,那么当你调用一个表单的构造函数时,就像创建任何其他对象时一样,只需将它指定为Owner参数,就像我在答案中的代码中演示的一样。为什么特别重视创建表单,而实际上它与创建任何其他类的实例相同?调用CreateForm的唯一原因是您需要使用Application.MainForm进行设置。 – 2010-02-01 20:37:54

0

您引用的文章不正确。有几个有效的原因,你为什么要多次调用Application.CreateForm

1)数据模块:你可能希望所有的时间都可用。最好的方法是Application.CreateForm。我知道有几个主题数据模块的应用,例如客户,发票,地址来处理数据库的不同区域&整齐地封装了功能。所有这些都创建在.dpr

2)大,缓慢加载的东西(这是一个坏主意在&本身,但这些事情发生,并经常由支持程序员继承...)。将加载时间移动到应用程序启动中,就像您的示例代码以及启动画面更新一样。用户期望应用程序需要一段时间才能得到持续感谢我们的同事上的Microsoft Office工作的斯特林努力降低预期我们其余的人:)

所以吧,总之,不担心你的代码很好 - 但你可能会失去“FreeAndNil”的东西。但是小灵通打对话框类型的东西是最好的调用:

with TMyform.Create(nil) do 
try 
    //Setup 
    case ShowModal of 
    // Whatever return values you care about (if any) 
    end; 
finally 
    Free; 
end; 

短,甜,给点&最大限度地减少内存使用...

+0

您声称链接的文章不正确,实际上是非常不正确的。你给出的*原因数量都不是你想要调用'Application.CreateForm()'的原因。 Re 1)你可以简单地'Create()',传递任何'Owner'包括'Application'。 Re 2)你可以在主窗体的OnCreate事件中做第一件事,没有人会看到任何不同之处。 – mghie 2010-02-16 08:49:01

+1

您可以用不同的方式创建表单,但是您描述的方式涉及您必须编写额外的代码才能获得明显的收益。你为什么要故意用“非德尔菲”的方式做事?如果您在IDE中创建表单或Datamodule,Delphi可以帮助您在运行时使用它放入.dpr中的Application.Createform语句来创建它。在与德尔福合作的16年中,我还没有看到任何证据表明Application.Createform本质上是低效的或不适用于应用程序生命周期所需的表单,这似乎是作者的断言。 – mcottle 2010-02-16 09:44:42