There has recently been quite a lot of hubbub in the Delphi blogosphere about a blog posting by a guy called Branden Tanga, who was venting his frustration with Rad Studio 2007. In his latest post called Now I am pissed at CodeGear RAD and the follow up called More about me being pissed at CodeGear he describes a problem about the visiblity of a class "TestClass" and how he thought he solved it, just to find that his solution doesn't work, which he then again blames on RAD-Studio. I think I know what the issue is and tried to leave a comment on his block but Blogger won't let me, so here is my reply.
In a unit called "global" he declares a class called "TestClass":
unit global;
interface
type
TestClass = class
end;
In another unit he then tries to declare a variable of that class and it fails to compile:
unit SomeOther;
interface
uses
global;
implementation
/// ....
procedure someproc;
var
Test: TestClass;
begin
end;
The above should definitely compile but for him it didn't, so there must be more to it.
Since his "solution" was to change the declaration like this:
var
Test: global.TestClass;
My guess is that there was another unit in the uses clause, that declared a variable called "TestClass" thus causing a name collision:
unit Intruder;
implementation
uses
global;
var
TestClass: global.TestClass;
unit SomeOther;
interface
uses
global,
intruder;
implementation
/// ....
procedure someproc;
var
Test: TestClass;
begin
end;
(It might have been called something like "testClass" rather than "TestClass" (lower case "t"), which is common practice in the Java/C# world, I have been told, since these languages are case sensitive. Delph isn't, so this is the same identifier!) This will produce a compiler error because "Test" is declared with a type identifier that isn't a type but a variable name. Adding the "global" prefix solves this issue but only until you try to actually instantiate "Test":
procedure someproc;
var
Test: global.TestClass;
begin
Test := TestClass.Create;
end;
While this looks fine it is actually an error: It will not call "global.TestClass.Create" but rather "intruder.TestClass.Create" and this will fail - probably with an access violation - because "TestClass" (the variable) has not been initialized.
To solve that issue again, prefixing "global" would work:
procedure someproc;
var
Test: global.TestClass;
begin
Test := global.TestClass.Create;
end;
But of course, this will easily fall over again somewhere else, so the only real solution would be to resolve the name conflict by renaming at least one of the two. I'd suggest to rename the class adhering to the Delphi naming convention of prefixing classes with an upper case T:
unit global;
interface
type
TTestClass = class
end;
The above is pure guesswork - I seem to have misslaid my crystal ball - but I think this is the issue he is facing. It is caused by having an implicit global namespace that contains every identifier from every unit in the uses clause. Identifiers are introduced in the order of the units in the uses clause, so the last one overrides all previous ones. This can be convenient if you want to locally override a type declaration, but it can also cause hard to find errors, which is even aggravated if you are using the "with" statement.
Hi Branden,
For now, I won't charge any consultancy fees ;-), but if it turns to be correct, please link to this article. In the future I suggest using the Delphi newsgroups when facing a problem like this rather than blogging about it.
twm
This document was generated using AFT v5.096
|