在Delphi中声明公共全局变量

时间:2011-04-25 02:08:03

标签: delphi

假设我在delphi项目中有两个表单,我希望能够从form2访问form1的变量。是否有人声明,在form1中说一个'public'变量,可以从所有形式中读取?

我已尝试在公开声明中添加变量

    { 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的使用部分,但由于循环引用,我似乎无法做到这一点。如果可能的话,我想不要使用指针。

3 个答案:

答案 0 :(得分:22)

首先,最好假装全局根本不存在。如果你开始用全局变量的拐点编程,你将只是避免学习非常简单的技术,让你不用它们。你担心使用指针(实际上根本不会帮助你解决问题),但不关心使用实际上更危险的全局变量。

Globals很危险,因为:

  • 它们在共享它们的单元之间提供链接(一种称为紧耦合的概念)
  • 此链接是隐藏的,因为如果不检查代码的详细信息,链接的确切性质并不明显。
  • 您的代码将会出现副作用,也就是说当您在某个方法中执行某些操作时,其他意外情况也会发生。这增加了细微错误的可能性和复杂性。
  • 以上几点的累积效果是,即使你将一个项目分成20个单位,你的大脑仍然必须同时考虑所有20个单位 - 好像它们只有1个单位。这首先打破了将项目分解为较小的可管理单元的目的。
  • 您很快就会发现即使是中等规模的项目也很难维护。

循环参考

请查看我的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的全局变量,你也可以轻松地取消它。


免责声明:部分代码广泛接受封装原则,但仅供说明之用。

答案 1 :(得分:16)

首先,如果你必须使用全局变量(最好不要使用全局变量,正如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; 

答案 2 :(得分:1)

示例显示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;

<小时/> 任何程序功能类型变量可以是正在执行的单位的本地;
它还使intellisense(Ctrl + Space)从仅用于单元的声明中清除;