Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declare public global variable in Delphi

Tags:

delphi

Let's say I have two forms in a delphi project, I want to be able to access form1's variables from form2. Is there anyone to declare, say a 'public' variable in form1 which can be read from all forms?

I have tried putting a variable in the public declaration

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

and in form 2 i have

    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.

I then put 'Unit1' into the uses section on Form2, but it seems I can't do that due to a circular reference. I'd like to refrain from using pointers if possible.

like image 380
Skeela87 Avatar asked Apr 25 '11 02:04

Skeela87


3 Answers

First, it's better to pretend that globals don't exist at all. If you start out programming with the crutch of global variables, you'll just avoid learning the very simple techniques that allow you to do without them. You're worrying about using pointers (which actually wouldn't help your problem at all), but not concerned about using globals which are actually more dangerous.

Globals are dangerous because:

  • They provide a link between the units that share them (a concept called tight coupling)
  • This link is hidden in the sense that it's not obvious the exact nature of the link without examining the detail of the code.
  • Your code will become littered with side effects, which is to say that when you do something in a method, other unexpected things happen as well. This increases the possibility and complexity of subtle bugs.
  • The cumulative effect of the above points is that even if you break a project down into 20 units, your brain still has to think about all 20 units at the same time - as if they were only1 unit. This defeats the purpose of breaking your project down into smaller manageable units in the first place.
  • You'll quickly find that even medium sized projects start becoming very difficult to maintain.

Circular References

Please take a look at my answer to a previous question for thoughts on dealing with circular references more generally.

In your case, you simply need to move Unit1 from the interface uses to the implementation uses.

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;

Sharing data without globals

You'll notice that technically you're still using globals; albeit ones created by Delphi and not your own. Here's a technique you can use to share data in a more controlled fashion.

unit Unit3;

interface

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

Then in Form1 you add the following:

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;

Then in Form2 you do something slightly different because you want to use Form1's shared data not its own "shared data".

implementation

uses
  ...
  Unit3;

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

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

Finally, after creating both forms, you give Form2 a refernce to Form1's shared data:

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;

The above illustrates how easily you can do away with even Delphi's global variables.


Disclaimer: Some of the code goes agaisnt generally accepted encapsulation principles, but is for illustrative purposes only.

like image 140
Disillusioned Avatar answered Sep 20 '22 21:09

Disillusioned


First, if you must use globals (it's probably better not to use globals, as Craig has wisely pointed out) then you should put the globals you want to share in SharedGlobals.pas:

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

Now use that unit, from the two units you want to share access to that variable in. Alternatively, have both reference another object, which is named something sensible, and have that object be designed, as the holder of state (variable values) that those two instances (forms or classes, or whatever) need to share.

Second idea, since your two units that you have already have dependencies on each other, you could also get around your circular dependency by using the unit that would create a circular dependency, from the implementation section instead of the interface:

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

...

 unit Unit1;
 interface
   /// stuff
 implementation
    uses Unit2; 
like image 37
Warren P Avatar answered Sep 23 '22 21:09

Warren P


the example shows Form1(main) and Form2(other) which has better use for organization;


FORM1 interface declaration only;

can be read from all forms;

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 and implementation declarations;

implementation declaration are local to the unit; cannot be read from all forms;

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;


any procedure, function, type, variable can be local to the unit under implementation;
it also makes intellisense(Ctrl+Space) clean from declarations used only to the unit;
like image 26
H3sDW11e Avatar answered Sep 23 '22 21:09

H3sDW11e