【问题标题】:Declare public global variable in Delphi在 Delphi 中声明公共全局变量
【发布时间】:2011-08-12 02:07:38
【问题描述】:

假设我在一个 delphi 项目中有两个表单,我希望能够从 form2 访问 form1 的变量。有没有人可以在 form1 中声明一个可以从所有表单中读取的“公共”变量?

我尝试在公共声明中放置一个变量

    { private declarations }
  public
    { public declarations }
  test: integer;
  end;     

在表格 2 中我有

    unit Unit2;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, unit1;

type

  { TForm2 }

  TForm2 = class(TForm)
    procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end; 

var
  Form2: TForm2; 
implementation

{$R *.lfm}

{ TForm2 }

procedure TForm2.FormCreate(Sender: TObject);
begin
  form1 //<---------- DOES NOT GET RECOGNIZED
end;

end.

然后我将“Unit1”放入 Form2 的使用部分,但由于循环引用,我似乎无法这样做。如果可能的话,我想避免使用指针。

【问题讨论】:

  • 您需要考虑为什么需要这些全局变量,以及是否可以不使用它们。通常您希望尽量减少公共全局变量的数量,因为它们对“错误”提供的保护很少(例如,因为它们是公共的并且可以读写,所以任何东西都可以覆盖它们)。

标签: delphi


【解决方案1】:

首先,最好假装全局变量根本不存在。如果您从全局变量的拐杖开始编程,您将避免学习允许您不使用它们的非常简单的技术。您担心使用指针(这实际上对您的问题没有任何帮助),但并不担心使用实际上更危险的全局变量。

全局变量很危险,因为:

  • 它们在共享它们的单元之间提供链接(称为紧密耦合的概念)
  • 此链接是隐藏的,因为如果不检查代码的详细信息,链接的确切性质并不明显。
  • 您的代码将充满副作用,也就是说,当您在方法中执行某项操作时,也会发生其他意想不到的事情。这增加了细微错误的可能性和复杂性。
  • 以上几点的累积效应是,即使你将一个项目分解为 20 个单元,你的大脑仍然必须同时考虑所有 20 个单元——就好像它们只有一个单元一样。这违背了将项目分解为更小的可管理单元的初衷。
  • 您很快就会发现,即使是中型项目也开始变得非常难以维护。

循环引用

请查看我的answer to a previous question,了解有关更一般地处理循环引用的想法。

在您的情况下,您只需将 Unit1interface 使用移动到 implementation 使用。

var
  Form2: TForm2; 

implementation

uses
  Unit1;

{$R *.lfm}

{ TForm2 }

procedure TForm2.FormCreate(Sender: TObject);
begin
  Form1.test; //Is now accessible, but remember: it will cause a runtime error if Form1 hasn't been created or has already been destroyed.
end;

在没有全局变量的情况下共享数据

您会注意到,从技术上讲,您仍在使用全局变量;尽管是由 Delphi 创建的,而不是您自己的。您可以使用以下技术以更可控的方式共享数据。

unit Unit3;

interface

type
  TSharedData = class(TObject)
  public
    Test1: Integer;
    Test2: Integer;
  end;

然后在Form1 中添加以下内容:

implementation

uses
  ...
  Unit3;

type
  TForm1 = class(TForm)
  ...
  public
    SharedData: TSharedData;
  end;

//Inside either the constructor or OnCreate event add the following line:
SharedData := TSharedData.Create;
//Inside either the destructor or OnDestroyevent add the following line:
SharedData.Free;

然后在Form2 中你会做一些稍微不同的事情,因为你想使用 Form1 的共享数据而不是它自己的“共享数据”。

implementation

uses
  ...
  Unit3;

type
  TForm2 = class(TForm)
  ...
  public
    Form1SharedData: TSharedData;
  end;

//Nothing added to constructor/destructor or OnCreate/OnDestroy events

最后,在创建了两个表单之后,你给Form2 一个引用Form1 的共享数据:

procedure RunApplicationWithoutFormGlobals;
var
  LForm1: TForm1;
  LForm2: TForm2;
begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, LForm1);
  Application.CreateForm(TForm2, LForm2);
  LForm2.Form1SharedData := LForm1.SharedData;
  Application.Run;
end;

以上说明了即使是 Delphi 的全局变量,您也可以轻松地取消。


免责声明:某些代码违反了普遍接受的封装原则,但仅用于说明目的。

【讨论】:

  • 在最后一个代码块中,我认为TForm1 = class(TForm) 应该是TForm2 = class(TForm)
  • 更好吗?现在您必须记住将 SharedData 分配给 form2!!!!
【解决方案2】:

首先,如果您必须使用全局变量(最好不要使用全局变量,正如 Craig 明智地指出的那样),那么您应该将要共享的全局变量放在 SharedGlobals.pas 中:

unit SharedGlobals;
interface
var
   {variables here}
   Something:Integer;
implementation
    { nothing here?}

现在使用该单元,从您想要共享访问该变量的两个单元中。或者,都引用另一个对象,该对象被命名为合理的,并将该对象设计为状态的持有者(变量值)这两个实例(表单或类,或其他)需要共享。

第二个想法,由于您的两个单元已经相互依赖,您还可以通过使用从实现部分创建循环依赖的单元来绕过循环依赖而不是界面:

 unit Unit2;
 interface
   /// stuff
 implementation
    uses Unit1; 

...

 unit Unit1;
 interface
   /// stuff
 implementation
    uses Unit2; 

【讨论】:

  • 效果很好,谢谢 =) 现在我只需要找到一种方法来做同样的事情,但彼此之间打开/关闭表单。
  • 更正:首先,最好假装全局变量根本不存在。如果您从全局变量的拐杖开始编程,您将避免学习允许您不用它们的非常简单的技术。
  • 全局变量确实倾向于违背面向对象编程思想的精神。好思想的克雷格。我会稍微修改一下我的答案。
【解决方案3】:

该示例显示了 Form1(main) 和 Form2(other) 更好地用于组织;


FORM1 仅接口声明;

可以从所有表格中读取;

interface

procedure oncreate(Sender: TObject);

implementation
uses Form2;

procedure TForm1.oncreate(Sender: TObject);
begin
  Form2.test1;
  //{Form2.test2} are not visible to Form1;
end;

FORM2接口和实现声明;

实现声明是单元本地的;不能从所有表格中读取;

interface

procedure test1;

implementation

procedure test2; //declared under implementation;

procedure TForm2.test1;
begin
  ShowMessage('form2 test1 message');
end;

procedure TForm2.test2;
begin
  ShowMessage('form2 test2 message');
end;


任何procedurefunctiontypevariable都可以是正在实施的单元的本地;
它还使智能感知(Ctrl+Space)从仅用于单元的声明中清除;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-27
    • 2014-12-03
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    相关资源
    最近更新 更多