在Delphi中声明公共全局变量



假设我在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的使用部分,但由于循环引用,我似乎不能这样做。我希望尽可能避免使用指针。

首先最好假装全局变量根本不存在。如果您开始使用全局变量作为拐杖进行编程,那么您将避免学习非常简单的技术,而这些技术允许您不使用全局变量。你担心使用指针(这实际上根本解决不了问题),却不担心使用全局变量,因为全局变量实际上更危险。

全局变量很危险,因为:

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

循环引用

请看看我对上一个问题的回答,了解如何更普遍地处理循环引用。

在您的情况下,您只需要将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的全局变量。


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

首先,如果您必须使用全局变量(最好不要使用全局变量,正如Craig明智地指出的那样),那么您应该将您想要共享的全局变量放在sharedglobal .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; 

这个例子展示了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;


任何过程函数类型变量都可以是要实现的单元的局部;
它还使智能感知(Ctrl+Space)从仅用于单元的声明中清除;

最新更新