Declaring block level variables for branches in delphi - delphi

In Delphi prism we can declare variables that is only needed in special occasions.
eg: In prism
If acondition then
begin
var a :Integer;
end;
a := 3; //this line will produce error. because a will be created only when the condition is true
Here 'a' cannot be assigned with 3 because it is nested inside a branch.
How can we declare a variable which can be used only inside a branch in delphi win32. So i can reduce memory usage as it is only created if a certain condition is true;
If reduced memory usage is not a problem what are the draw backs we have (or we don't have)

The premise of your question is faulty. You're assuming that in languages where block-level variables are allowed, the program allocates and releases memory for those variable when control enters or leaves those variables' scopes. So, for example, you think that when acondition is true, the program adjusts the stack to make room for the a variable as it enters that block. But you're wrong.
Compilers calculate the maximum space required for all declared variables and temporary variables, and then they reserve that much space upon entry to the function. Allocating that space is as simple as adjusting the stack pointer; the time required usually has nothing to do with the amount of space being reserved. The bottom line is that your idea won't actually save any space.
The real advantage to having block-level variables is that their scopes are limited.
If you really need certain variables to be valid in only one branch of code, then factor that branch out to a separate function and put your variables there.

The concept of Local Variable Declaration Statements like in Java is not supported in Delphi, but you could declare a sub-procedure:
procedure foo(const acondition: boolean);
procedure subFoo;
var
a: integer;
begin
a := 3;
end;
begin
If acondition then
begin
subFoo;
end;
end;

There is no way in Delphi to limit scope of an variable to less than entire routine. And in case of a single integer variable it doesn't make sense to worry about it... But in case of large data structure you should allocate it dynamically, not statically, ie instead of
var integers: array[1..10000]of Integer;
use
type TIntArray: array of Integer;
var integers: TIntArray;
If acondition then
begin
SetLength(integers, 10000);
...
end;

Beware that it could only be "syntactic sugar". The compiler may ensure you don't use the variable outside the inner scope, but that doesn't mean it could save memory. The variable may be allocated on the stack in the procedure entry code anyway, regardless if it is actually used or not. AFAIK most ABI initialize the stack on entry and clean it on exit. Manipulating the stack in a much more complex way while the function is executing including taking care of different execution paths may be even less performant - instead of a single instruction to reserve stack space you need several instruction scattered along code, and ensure the stack is restored correctly adding more, epecially stack unwinding due to an exception may become far more complex.
If the aim is to write "better" code because of better scope handling to ensure the wrong variable is not used in the wrong place it could be useful, but if you need it as a way to save memory it could not be the right way.

You can emulate block-level variables with the (dreaded) with statement plus a function returning a record. Here's a bit of sample code, written in the browser:
type TIntegerA = record
A: Integer;
end;
function varAInteger: TIntegerA;
begin
Result.A := 0;
end;
// Code using this pseudo-local-variable
if Condition then
with varAInteger do
begin
A := 7; // Works.
end
else
begin
A := 3; // Error, the compiler doesn't know who A is
end;
Edit to clarify this proposition
Please note this kind of wizardry is no actual replacement for true block-level variables: Even those they're likely allocated on stack, just like most other local variables, the compiler is not geared to treat them as such. It's not going to do the same optimizations: a returned record will always be stored in an actual memory location, while a true local variable might be associated with a CPU register. The compiler will also not let you use such variables for "for" statements, and that's a big problem.

Having commented all that - there is a party trick that Delphi has that has far more uses than a simple local variable and may achieve your aim:
function Something: Integer;
begin
// don't want any too long local variables...
If acondition then
asm
// now I have lots of 'local' variables available in the registers
mov EAX, #AnotherVariable //you can use pascal local variables too!
// do something with the number 3
Add EAX, 3
mov #Result, EAX
jmp #next
#AnotherVariable: dd 10
#next:
end;
end;
end;
:)) bit of a pointless example...

Related

When will a constant be created in Delphi XE2?

I am using Delphi XE2 and I have to write a function which needs some constant arrays. If I declare these inside the function, when will they be created? Each time when the function runs or only once?
Example:
function Something;
const
Arr: array[0..100] of string = ('string1', 'string2'...);
begin
end;
The array will occupy a fixed, constant, region in your process's memory for the duration of the process's life.
In fact, you can easily see that yourself:
Each time the timer fires, you'll find that Pointer(#Arr) has the same value, and at that point in memory, you'll find your constant array.
And if you change it using the Memory panel (like string1 to orange1), the change will persist.
Just to elaborate a little more on Andreas Rejbrand's answer...
If you declare a local constant, it'll remain in compilation even if you declare the same values more than once.
These constants will be independent. In other words, if you write:
function Something;
const
Arr: array[0..100] of string = ('string1', 'string2'...);
begin
end;
function Otherhing;
const
Values: array[0..100] of string = ('string1', 'string2'...);
begin
end;
You'll get two sets of "string1" and "string2" in your compiled code.
If you change (in file or memory) one "string1" to "orange1" in one function, the other "string1" will remain the same.
There's no compiler optimization to "unify" those constants.
Aside from being a waste of code, if you have the same constant declared twice, it can become a mess if you change your code in one place and forget to check everywhere else.
In summary, if you have more than one place in your program where you'll use a constant, always consider declaring them global (or in the unit's scope) not locally.

creating a fast case statement (assembly)

I have a project that has intensive use of the case statement with many procedures coming off it. I know you can place case statements in a two tear arrangement divide in blocks of 10 and a second case statement to separate individual procedures. But I have a better idea if I can pull it off.
I want to call it assembly case
Prolist: array [1..500] of Pointer =
(#Procedure1, #Procedure2, #Procedure3, #Procedure4, #Procedure5);
Procedure ASMCase(Prolist: array of Pointer; No: Word; Var InRange: Boolean);
var Count : DWord;
PTR: Pointer;
Pro : Procedure;
begin
Count := No * 4;
InRange := boolean(Count <= SizeOf(Prolist));
If not InRange then Exit;
PTR := Pointer(DWord(#Prolist[1]) + Count);
If PTR <> nil then Pro := #PTR else Exit;
Pro; /run procedure
end;​
The point is I'm creating a direct jump to the procedure.
In my case procedures can have an identical header and global data can be accessed for any odd information. Writing it in assembly would be faster I think but what I'm not sure on is running the procedures. Please do not ask why am I doing this as I have 500 procedures with many calls on the case statement and time is of essence with a fast processor.
It's expensive to pass that array by value. Pass it by const.
I can't see the point of the InRange flag and test. Don't pass out of range indices. And if you have to test, do it right. Don't use SizeOf which measures byte size. Use high or perhaps Length, if you have to. Which I doubt.
The pointer assignment test (PTR <> nil) is bogus. That condition always evaluates true. And the array indexing is very weird. What's wrong with []?
On top of that, your array is 1-based (usually a bad choice) but open arrays are always 0-based. Likely that's going to trip you up.
In short, I'd throw away all of that code. It's both wrong and needless. I'd just write it like this:
ProList[No]();
In order for this to compile your array would need to be defined as an array of procedural type rather than array of Pointer. Adding some type safety would be a good move.
It's pretty hard to see asm making much difference here. The compiler is going to emit optimal code.
If you are concerned with out of bound access, enable range checking in debug mode. Disable it for release if performance is paramount.
Bear in mind that global data structures don't tend to scale well as you add complexity. Most experienced programmers go to some length to avoid global state. Are you sure that global state is the right choice for you?
If you do need to improve performance, first identify opportunity for improvement. Reading from an array and calling a function are not likely candidates. Look at the procedures that you call. The bottlenecks are surely there.
One final point. Try to forget that you ever learn to use # with function pointers. Doing so yields an untyped pointer, of type Pointer that can be assigned to any pointer type. And thus you completely abandon type checking. Your procedure could have the wrong signature altogether and the compiler is not able to tell you. Declare your array of procedures with a type safe procedure type.

Why most Delphi examples use FillChar() to initialize records?

I just wondered, why most Delphi examples use FillChar() to initialize records.
type
TFoo = record
i: Integer;
s: string; // not safe in record, better use PChar instead
end;
const
EmptyFoo: TFoo = (i: 0; s: '');
procedure Test;
var
Foo: TFoo;
s2: string;
begin
Foo := EmptyFoo; // initialize a record
// Danger code starts
FillChar(Foo, SizeOf(Foo), #0);
s2 := Copy("Leak Test", 1, MaxInt); // The refcount of the string buffer = 1
Foo.s = s2; // The refcount of s2 = 2
FillChar(Foo, SizeOf(Foo), #0); // The refcount is expected to be 1, but it is still 2
end;
// After exiting the procedure, the string buffer still has 1 reference. This string buffer is regarded as a memory leak.
Here (http://stanleyxu2005.blogspot.com/2008/01/potential-memory-leak-by-initializing.html) is my note on this topic. IMO, declare a constant with default value is a better way.
Historical reasons, mostly. FillChar() dates back to the Turbo Pascal days and was used for such purposes. The name is really a bit of a misnomer because while it says FillChar(), it is really FillByte(). The reason is that the last parameter can take a char or a byte. So FillChar(Foo, SizeOf(Foo), #0) and FillChar(Foo, SizeOf(Foo), 0) are equivalent. Another source of confusion is that as of Delphi 2009, FillChar still only fills bytes even though Char is equivalent to WideChar. While looking at the most common uses for FillChar in order to determine whether most folks use FillChar to actually fill memory with character data or just use it to initialize memory with some given byte value, we found that it was the latter case that dominated its use rather than the former. With that we decided to keep FillChar byte-centric.
It is true that clearing a record with FillChar that contains a field declared using one of the "managed" types (strings, Variant, Interface, dynamic arrays) can be unsafe if not used in the proper context. In the example you gave, however, it is actually safe to call FillChar on the locally declared record variable as long as it is the first thing you ever do to the record within that scope. The reason is that the compiler has generated code to initialize the string field in the record. This will have already set the string field to 0 (nil). Calling FillChar(Foo, SizeOf(Foo), 0) will just overwrite the whole record with 0 bytes, including the string field which is already 0. Using FillChar on the record variable after a value was assigned to the string field, is not recommended. Using your initialized constant technique is a very good solution this problem because the compiler can generate the proper code to ensure the existing record values are properly finalized during the assignment.
If you have Delphi 2009 and later, use the Default call to initialize a record.
Foo := Default(TFoo);
See David's answer to the question How to properly free records that contain various types in Delphi at once?.
Edit:
The advantage of using the Default(TSomeType) call, is that the record is finalized before it is cleared. No memory leaks and no explicit dangerous low level call to FillChar or ZeroMem. When the records are complex, perhaps containing nested records etc, the risk of making mistakes is eliminated.
Your method to initialize the records can be made even simpler:
const EmptyFoo : TFoo = ();
...
Foo := EmptyFoo; // Initialize Foo
Sometimes you want a parameter to have a non-default value, then do like this:
const PresetFoo : TFoo = (s : 'Non-Default'); // Only s has a non-default value
This will save some typing and the focus is set on the important stuff.
FillChar is fine to make sure you don't get any garbage in a new, uninitialized structure (record, buffer, arrray...).
It should not be used to "reset" the values without knowing what your are resetting.
No more than just writing MyObject := nil and expecting to avoid a memory leak.
In particulart all managed types are to be watched carefully.
See the Finalize function.
When you have the power to fiddle directly with the memory, there is always a way to shoot yourself in the foot.
FillChar is usually used to fill Arrays or records with only numeric types and array. You are correct that it shouldn't be used to when there are strings (or any ref-counted variables) in the record.
Although your suggestion of using a const to initialize it would work, an issue comes into play when I have a variable length array that I want to initialize.
The question may also be asking:
why FillChar
and not ZeroMemory?
There is no ZeroMemory function in Windows. In the header files (winbase.h) it is a macro that, in the C world, turns around and calls memset:
memset(Destination, 0, Length);
ZeroMemory is the language neutral term for "your platform's function that can be used to zero memory"
The Delphi equivalent of memset is FillChar.
Since Delphi doesn't have macros (and before the days of inlining), calling ZeroMemory meant you had to suffer the penalty of an extra function call before you actually got to FillChar.
So in many ways, calling FillChar is a performance micro-optimization - which no longer exists now that ZeroMemory is inlined:
procedure ZeroMemory(Destination: Pointer; Length: NativeUInt); inline;
Bonus Reading
Windows also contains the SecureZeroMemory function. It does the exact same thing as ZeroMemory. If it does the same thing as ZeroMemory, why does it exist?
Because some smart C/C++ compilers might recognize that setting memory to 0 before getting rid of the memory is a waste of time - and optimize away the call to ZeroMemory.
I don't think Delphi's compiler is as smart as many other compilers; so there's no need for a SecureFillChar.
Traditionally, a character is a single byte (no longer true for Delphi 2009), so using fillchar with a #0 would initalize the memory allocated so that it only contained nulls, or byte 0, or bin 00000000.
You should instead use the ZeroMemory function for compatibility, which has the same calling parameters as the old fillchar.
This question has a broader implication that has been in my mind for ages. I too, was brought up on using FillChar for records. This is nice because we often add new fields to the (data) record and of course FillChar( Rec, SizeOf( Rec), #0 ) takes care of such new fields. If we 'do it properly', we have to iterate through all fields of the record, some of which are enumerated types, some of which may be records themselves and the resulting code is less readable as well be possibly erroneous if we dont add new record fields to it diligently. String fields are common, thus FillChar is a no-no now. A few months ago, I went around and converted all my FillChars on records with string fields to iterated clearing, but I was not happy with the solution and wonder if there is a neat way of doing the 'Fill' on simply types (ordinal / float) and 'Finalize' on variants and strings?
Here is a better way to initialize stuff without using FillChar:
Record in record (Cannot initialize)
How to initialize a static array?

Using Pointers in Delphi

I have been developing for some time now, and I have not used pointers in my development so far.
So what are the benefits of pointers? Does an application run faster or uses fewer resources?
Because I am sure that pointers are important, can you “point” me to some articles, basic but good to start using pointers in Delphi? Google gives me too many, too special results.
A pointer is a variable that points to a piece of memory. The advantages are:
you can give that piece of memory the size you want.
you only have to change a pointer to point to a different piece of memory which saves a lot of time copying.
Delphi uses a lot of hidden pointers. For example, if you are using:
var
myClass : TMyClass;
begin
myClass := TMyClass.Create;
myClass is a pointer to the object.
An other example is the dynamic array. This is also a pointer.
To understand more about pointers, you need to understand more about memory. Each piece of data can exist in different pieces of data.
For example global variables:
unit X;
interface
var
MyVar: Integer;
A global variable is defined in the datasegment. The datasegment is fixed. And during the lifetime of the program these variables are available. Which means the memory can not be used for other uses.
Local variables:
procedure Test;
var
MyVar: Integer;
A local variable exists on the stack. This is a piece of memory that is used for housekeeping. It contains the parameters for the function (ok some are put in a register but that is not important now). It contains the return adress so the cpu knows where to return if the program has ended. And it contains the local variables used in the functions.
Local variables only exists during the lifetime of a function. If the function is ended, you can't access the local variable in a reliable way.
Heap variables:
procedure Test2;
var
MyClass: TMyClass;
begin
MyClass := TMyClass.Create;
The variable MyClass is a pointer (which is a local variable that is defined on the stack). By constructing an object you allocate a piece of memory on the heap (the large piece of 'other' memory that is not used for programs and stacks). The variable MyClass contains the address of this piece of memory.
Heap variables exist until you release them. That means that if you exit the funcion Test2 without freeing the object, the object still exists on the heap. But you won't be able to access it because the address (variable MyClass) is gone.
Best practices
It is almost always preferably to allocate and deallocate a pointer variable at the same level.
For example:
var
myClass: TMyClass;
begin
myClass := TMyClass.Create;
try
DoSomething(myClass);
DoSomeOtherthing(myClass);
finally
myClass.Free;
end;
end;
If you can, try to avoid functions that return an instance of an object. It is never certain if the caller needs to dispose of the object. And this creates memory leaks or crashes.
You have been given a lot of good answers so far, but starting with the answer that you are already dealing with pointers when you use long strings, dynamic arrays and object references you should start to wonder why you would use pointers, instead of long strings, dynamic arrays and object references. Is there any reason to still use pointers, given that Delphi does a good job hiding them from you, in many cases?
Let me give you two examples of pointer use in Delphi. You will see that this is probably not at all relevant for you if you mostly write business apps. It can however become important if you ever need to use Windows or third party API functions that are not imported by any of the standard Delphi units, and for which no import units in (for example) the JEDI libraries can be found. And it may be the key to achieve that necessary last bit of speed in string-processing code.
Pointers can be used to deal with data types of varying sizes (unknown at compile time)
Consider the Windows bitmap data type. Each image can have different width and height, and there are different formats ranging from black and white (1 bit per pixel) over 2^4, 2^8, 2^16, 2^24 or even 2^32 gray values or colours. That means that it is unknown at compile time how much memory a bitmap will occupy.
In windows.pas there is the TBitmapInfo type:
type
PBitmapInfo = ^TBitmapInfo;
tagBITMAPINFO = packed record
bmiHeader: TBitmapInfoHeader;
bmiColors: array[0..0] of TRGBQuad;
end;
TBitmapInfo = tagBITMAPINFO;
The TRGBQuad element describes a single pixel, but the bitmap does of course contain more than one pixel. Therefore one would never use a local variable of type TBitmapInfo, but always a pointer to it:
var
BmpInfo: PBitmapInfo;
begin
// some other code determines width and height...
...
BmpInfo := AllocMem(SizeOf(TBitmapInfoHeader)
+ BmpWidth * BmpHeight * SizeOf(TRGBQuad));
...
end;
Now using the pointer you can access all pixels, even though TBitmapInfo does only have a single one. Note that for such code you have to disable range checking.
Stuff like that can of course also be handled with the TMemoryStream class, which is basically a friendly wrapper around a pointer to a block of memory.
And of course it is much easier to simply create a TBitmap and assign its width, height and pixel format. To state it again, the Delphi VCL does eliminate most cases where pointers would otherwise be necessary.
Pointers to characters can be used to speed up string operations
This is, like most micro optimizations, something to be used only in extreme cases, after you have profiled and found the code using strings to consume much time.
A nice property of strings is that they are reference-counted. Copying them does not copy the memory they occupy, it only increases the reference count instead. Only when the code tries to modify a string which has a reference count greater than 1 will the memory be copied, to create a string with a reference count of 1, which can then safely be modified.
A not-so-nice property of strings is that they are reference-counted. Every operation that could possibly modify the string has to make sure that the reference count is 1, because otherwise modifications to the string would be dangerous. Replacing a character in a string is such a modification. To make sure that the reference count is 1 a call to UniqueString() is added by the compiler whenever a character in a string is written to. Now writing n characters of a string in a loop will cause UniqueString() to be called n times, even though after the first time is is assured that the reference count is 1. This means basically n - 1 calls of UniqueString() are performed unnecessarily.
Using a pointer to the characters is a common way to speed up string operations that involve loops. Imagine you want (for display purposes) to replace all spaces in a string with a small dot. Use the CPU view of the debugger and compare the code executed for this code
procedure MakeSpacesVisible(const AValue: AnsiString): AnsiString;
var
i: integer;
begin
Result := AValue;
for i := 1 to Length(Result) do begin
if Result[i] = ' ' then
Result[i] := $B7;
end;
end;
with this code
procedure MakeSpacesVisible(const AValue: AnsiString): AnsiString;
var
P: PAnsiChar;
begin
Result := AValue;
P := PAnsiChar(Result);
while P[0] <> #0 do begin
if P[0] = ' ' then
P[0] := $B7;
Inc(P);
end;
end;
In the second function there will be only one call to UniqueString(), when the address of the first string character is assigned to the char pointer.
You probably have used pointers, but you just don't know it. A class variable is a pointer, a string is a pointer, a dynamic array is a pointer, Delphi just hides it for you. You will see them when you are performing API calls (casting strings to PChar), but even then Delphi can hide a lot.
See Gamecats answer for advantages of pointers.
In this About.com article you can find a basic explanation of pointers in Delphi.
Pointers are necessary for some data structures. The simplest example is a linked list. The advantage of such structures is that you can recombine elements without moving them in memory. For example you can have a linked list of large complex objects and swap any two of them very quickly because you really have to adjust two pointers instead of moving this objects.
This applies to many languages including Object Pascal (Delphi).

Why should I not use "with" in Delphi?

I've heard many programmers, particularly Delphi programmers scorn the use of 'with'.
I thought it made programs run faster (only one reference to parent object) and that it was easier to read the code if used sensibly (less than a dozen lines of code and no nesting).
Here's an example:
procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
with ARect do FillRectS(Left, Top, Right, Bottom, Value);
end;
I like using with. What's wrong with me?
One annoyance with using with is that the debugger can't handle it. So it makes debugging more difficult.
A bigger problem is that it is less easy to read the code. Especially if the with statement is a bit longer.
procedure TMyForm.ButtonClick(...)
begin
with OtherForm do begin
Left := 10;
Top := 20;
CallThisFunction;
end;
end;
Which Form's CallThisFunction will be called? Self (TMyForm) or OtherForm? You can't know without checking if OtherForm has a CallThisFunction method.
And the biggest problem is that you can make bugs easy without even knowing it. What if both TMyForm and OtherForm have a CallThisFunction, but it's private. You might expect/want the OtherForm.CallThisFunction to be called, but it really is not. The compiler would have warned you if you didn't use the with, but now it doesn't.
Using multiple objects in the with multiplies the problems. See http://blog.marcocantu.com/blog/with_harmful.html
I prefer the VB syntax in this case because here, you need to prefix the members inside the with block with a . to avoid ambiguities:
With obj
.Left = 10
.Submit()
End With
But really, there's nothing wrong with with in general.
It would be great if the with statement would be extented the following way:
with x := ARect do
begin
x.Left := 0;
x.Rigth := 0;
...
end;
You wouldn't need to declare a variable 'x'. It will be created by the compiler. It's quick to write and no confusion, which function is used.
It is not likely that "with" would make the code run faster, it is more likely that the compiler would compile it to the same executable code.
The main reason people don't like "with" is that it can introduce confusion about namespace scope and precedence.
There are cases when this is a real issue, and cases when this is a non-issue (non-issue cases would be as described in the question as "used sensibly").
Because of the possible confusion, some developers choose to refrain from using "with" completely, even in cases where there may not be such confusion. This may seem dogmatic, however it can be argued that as code changes and grows, the use of "with" may remain even after code has been modified to an extent that would make the "with" confusing, and thus it is best not to introduce its use in the first place.
In fact:
procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
with ARect do FillRectS(Left, Top, Right, Bottom, Value);
end;
and
procedure TBitmap32.FillRectS(const ARect: TRect; Value: TColor32);
begin
FillRectS(ARect.Left, ARect.Top, ARect.Right, ARect.Bottom, Value);
end;
Will generate exactly the same assembler code.
The performance penalty can exist if the value of the with clause is a function or a method. In this case, if you want to have good maintenance AND good speed, just do what the compiler does behind the scene, i.e. create a temporary variable.
In fact:
with MyRect do
begin
Left := 0;
Right := 0;
end;
is encoded in pseudo-code as such by the compiler:
var aRect: ^TRect;
aRect := #MyRect;
aRect^.Left := 0;
aRect^.Right := 0;
Then aRect can be just a CPU register, but can also be a true temporary variable on stack. Of course, I use pointers here since TRect is a record. It is more direct for objects, since they already are pointers.
Personally, I used with sometimes in my code, but I almost check every time the asm generated to ensure that it does what it should. Not everyone is able or has the time to do it, so IMHO a local variable is a good alternative to with.
I really do not like such code:
for i := 0 to ObjList.Count-1 do
for j := 0 to ObjList[i].NestedList.Count-1 do
begin
ObjList[i].NestedList[j].Member := 'Toto';
ObjList[i].NestedList[j].Count := 10;
end;
It is still pretty readable with with:
for i := 0 to ObjList.Count-1 do
for j := 0 to ObjList[i].NestedList.Count-1 do
with ObjList[i].NestedList[j] do
begin
Member := 'Toto';
Count := 10;
end;
or even
for i := 0 to ObjList.Count-1 do
with ObjList[i] do
for j := 0 to NestedList.Count-1 do
with NestedList[j] do
begin
Member := 'Toto';
Count := 10;
end;
but if the inner loop is huge, a local variable does make sense:
for i := 0 to ObjList.Count-1 do
begin
Obj := ObjList[i];
for j := 0 to Obj.NestedList.Count-1 do
begin
Nested := Obj.NestedList[j];
Nested.Member := 'Toto';
Nested.Count := 10;
end;
end;
This code won't be slower than with: compiler does it in fact behind the scene!
By the way, it will allow easier debugging: you can put a breakpoint, then point your mouse on Obj or Nested directly to get the internal values.
What you save in typing, you lose in readability.
Many debuggers won't have a clue what you're referring to either so debugging is more difficult.
It doesn't make programs run faster.
Consider making the code within your with statement a method of the object that you're refering to.
It's primarily a maintenance issue.
The idea of WITH makes reasonable sense from a language point of view, and the argument that it keeps code, when used sensibly, smaller and clearer has some validity. However the problem is that most commercial code will be maintained by several different people over it's lifetime, and what starts out as a small, easily parsed, construct when written can easily mutate over time into unwieldy large structures where the scope of the WITH is not easily parsed by the maintainer. This naturally tends to produce bugs, and difficult to find ones at that.
For example say we have a small function foo which contains three or four lines of code which have been wrapped inside a WITH block then there is indeed no issue. However a few years later this function may have expanded, under several programmers, into 40 or 50 lines of code still wrapped inside a WITH. This is now brittle, and is ripe for bugs to be introduced, particularly so if the maintainer stars introducing additional embedded WITH blocks.
WITH has no other benefits - code should be parsed exactly the same and run at the same speed (I did some experiments with this in D6 inside tight loops used for 3D rendering and I could find no difference). The inability of the debugger to handle it is also an issue - but one that should have been fixed a while back and would be worth ignoring if there were any benefit. Unfortunately there isn't.
This debate happens in Javascript a lot too.
Basically, that With syntax makes it very hard to tell at a glance which Left/Top/etc property/method you're calling on.You could have a local variable called Left, and a property (it's been a while since I've done delphi, sorry if the name is wrong) called Left, perhaps even a function called Left. Anyone reading the code who isn't super familiar with the ARect structure could be very very lost.
I do not like it because it makes debbuging a hassle. You cannot read the value of a variable or the like by just hovering over it with a mouse.
There's nothing wrong with it as long as you keep it simple and avoid ambiguities.
As far as I'm aware, it doesn't speed anything up though - it's purely syntactic sugar.
At work we give points for removing Withs from an existing Win 32 code base because of the extra effort needed to maintain code that uses them. I have found several bugs in a previous job where a local variable called BusinessComponent was masked by being within a With begin block for an object that a published property BusinessComponent of the same type. The compiler chose to use the published property and the code that meant to use the local variable crashed.
I have seen code like
With a,b,c,d do {except they are much longer names, just shortened here)
begin
i := xyz;
end;
It can be a real pain trying to locate where xyz comes from. If it was c, I'd much sooner write it as
i := c.xyz;
You think it's pretty trivial to understand this but not in a function that was 800 lines long that used a with right at the start!
You can combine with statements, so you end up with
with Object1, Object2, Object3 do
begin
//... Confusing statements here
end
And if you think that the debugger is confused by one with, I don't see how anyone can determine what is going on in the with block
It permits incompetent or evil programmers to write unreadble code. Therefor, only use this feature if you are neither incompetent nor evil.
... run faster ...
Not necessarily - your compiler/interpreter is generally better at optimizing code than you are.
I think it makes me say "yuck!" because it's lazy - when I'm reading code (particularly someone else's) I like to see explicit code. So I'd even write "this.field" instead of "field" in Java.
We've recently banned it in our Delphi coding stnadards.
The pros were frequently outweighing the cons.
That is bugs were being introduced because of its misuse. These didn't justify the savings in time to write or execute the code.
Yes, using with can led to (mildly) faster code execution.
In the following, foo is only evaluated once:
with foo do
begin
bar := 1;
bin := x;
box := 'abc';
end
But, here it is evaluated three times:
foo.bar := 1;
foo.bin := x;
foo.box := 'abc';
For Delphi 2005 is exist hard error in with-do statement - evaluate pointer is lost and repace with pointer up. There have to use a local variable, not object type directly.

Resources