When I'm reading SO users' comments, I read many small useful tips, so I'm opening this thread to collect such tips.
For example, in this thread [1] Oliver Giesen says:
First of all: Don't pass a non-nil Owner to Create if you're going to free the objects yourself anyway. This just adds lots of unnecessary overhead.
Install GExperts [1]. You may never use anything but their grep, but for that alone, it's worth it: fast, flexible, and a great UI for viewing the search results. It's a pity their regular expression support sucks so bad.
Always make your constructor's first line be "inherited Create(...)" and your destructor's last line be "inherited Destroy;". Don't try to get tricky and do things before inherited Create, or (worse yet) after inherited Destroy; it will bite you sooner or later. Call the inherited constructor/destructor even if you're descending from TObject and the inherited call isn't technically necessary. You may refactor later, and not be descending directly from TObject anymore, and wondering why you have memory leaks.
When you're trying to decide whether something should be a function or a read-only property, ask yourself, "Is this something I would want to hover over in the debugger and see its value?" If so, it's worth the extra overhead to make it a property.
Learn to use the full-debug-mode version of the FastMM memory manager.
Understand that Free and FreeAndNil do nil checks for you. You don't need to write "if Something <> nil then FreeAndNil(Something)"; just call FreeAndNil.
Write your destructors defensively. In particular, don't assume that all your fields have been initialized, even if they're all initialized in the constructor. If an exception got thrown during the constructor, then the destructor will run immediately, and some of your fields might still be nil. If your destructor throws an access violation, you just lost out on finding out what the original error was.
Never optimize unless you've proven you know where the bottleneck is. (Not Delphi-specific, but relevant given some of others' tips about making parameters const and the like.)
In keeping with the previous point, never mark an interface-type parameter as "const" unless you really, really know you need to, with the profile results to back it up. It's too easy (and perfectly reasonable!) to write code that calls MyMethod(TMyInterfacedObject.Create). If MyMethod's parameter was marked as const, you just leaked an instance of TMyInterfacedObject -- the const parameter tells the compiler "don't bother calling _AddRef and _Release", so the object never got freed.
Avoid parallel indexed properties (e.g. a Strings[] property and an Objects[] property where Strings[3] logically corresponds to Objects[3]). Instead, make one indexed property that contains an object with multiple properties. Centralizes your logic and makes the code cleaner in the long run.
Don't use primitive data types if they don't fit. Make your own custom data types using records with methods. For example, if you keep needing to operate on a month-and-year (but don't care about the day), don't try to pass a pair of integers all over the place, or a TDateTime with a day that you try to ignore. Make a TMonthAndYear instead. Give it constructors and/or factory methods. Give it properties. Overload the = operator. Add an overloaded CheckEquals to your base DUnit test case that can compare two TMonthAndYears. You'll be amazed at how much clearer your logic becomes.
Last but not least: I know there are those who would rather be programming in assembly than in Delphi, would rather write their own memory manager than use FastMM, cringe in terror at the very mention of garbage collection, and who will therefore flame me endlessly for this, but our shop's #1 Delphi tip, arrived at through years of experience, is: Unless you have a good reason not to, interface everything. Don't waste brain cycles thinking about memory management; it is not worth it. Perhaps 0.5% of our interfaces have become performance problems, and (with profile results) we've been able to address those. And they make coding so much easier. Make a function that returns an interface instead of populating half a dozen out parameters, and use that result all you want but never have to free it. Get rid of the inevitable try .. finally .. FreeAndNil, and those endless chains of FreeAndNils in your destructors (did you remember them all?). Pass an object around with confidence that it'll get cleaned up when the last person is done with it. And curse the Delphi developers for forcing you to choose between code that's easy to browse through and code that's easy to maintain.
[1] http://www.gexperts.org/As a Delphi 2009 user turn off the StringChecks option in the project settings. This causes an immense performance loss and code bloat if it is on (what it is by default). It is only needed due to the lack of a C++Builder unicode migration tool, but all Delphi users have to pay for it.
Don't use "raise E;"
on E: Exception do
begin
...
raise E;
end;
This will lead to an access violation because E is destroyed in the "end;". Change the code to "raise;" which calls the RTL function System.RaiseAgain instead of System.RaiseExcept(E)
Use "const" for managed types (string, dynamic array, variant, interface). This will increase the speed of your application.
Don't call the constructor after "try" because the variable will be uninitialized in the "finally" if the constructor throws an exception. Alternatively you can assign "nil" to the variable before the "try" and then call the constructor in the "try".
Don't use strings for buffers. Strings are String and not Byte-Arrays. Especially if you plan to migrate your code to Delphi 2009 where SizeOf(Char) <> SizeOf(Byte).
Don't combine "not" with a relation ("if not i <> 2") because "not" has a higher evaluation priority and will execute a bitwise not before the relation is evaluated resulting in odd results. Furthermore every relation operator has its counter-operator which makes the usage of "not" unnecessary.
Use the overloaded Date/Time/Float functions with a self defined FormatSettings parameter if you write or read from files. If one of your customers use different format settings (e.g. "," as decimal separator), your application will fail otherwise.
I usually crucify programmers that don't put begin on a newline. That messes too much with my ability to read the code. Senior programmer's privilege. For the rest I'm pretty lax, stylewise.
Warning: Some open doors below:
For multi platform use and to future proof against future Delphi changes:
(*) when are we finally going to get high and low for sets, so I can kill this exception?
Added later:
whenever it is possible use FreeAndNil instead of object.free for more refer to Why should you always use FreeAndNil instead of Free. [1]
[1] http://blog.eurekalog.com/?p=135Don't use with. Ever. It may make your code appear a bit cleaner and easier to read, but that comes at the expense of debuggability. (Is that a word?) There's no way to tell the compiler what's supposed to use the with and what isn't, so you can end up with a mess like this:
procedure TMyForm.button1Click(Sender: TObject);
begin
with Edit1 do
Caption := 'Hello World!';
end;
Looks good, right? Except that TEdit doesn't have a Caption property; it has a Text property. But this will still compile, because your Form has a Caption property, and you end up doing something completely different than you expected.
With would be a whole lot better if its syntax worked the way VB does it, but in its current form it's too dangerous to be actually useful. Consider it syntactic NutraSweet: tastes a whole lot like syntactic sugar, but it leaves a weird aftertaste and it's actually bad for your health in the long term.
With
is one of the sweetest little Delphi features and I miss it ever so much in Java. @Mihai - ever needed to draw on a canvas? with image1.picture.bitmap.canvas do begin brush.color:=clBlack; fillrect(cliprect); etc... end; Now that's GREAT - Peter Perháč
Ok, at the risk of getting flamed down, I think somebody has to defend the poor "with" statement, for the sake of discussion. It seems to be the latest thing to bash "with". Damnit, I'm so out of fashion.
Somebody suggested to never ever use "with". To me, that seems to be a bit too paranoid.
Along the same logic, you should never ever use units or classes or records either. You might get a name-conflict/confusion with that too. Consider this example:
unit Unit1;
interface
uses Windows; // contains a function called "LoadModule(...)"
type
Windows=class
class procedure LoadModule(...);
end;
implementation
class procedure Windows.LoadModule(...);
begin
// format c: here
end;
initialization
Windows.LoadModule(...);
end.
Oh no! Name-conflict/confusing code!
What does this code do? Load a module, or format my harddrive?
Well, who knows.. But would you advice never ever to use multiple units or create classes because somebody could create a naming conflict or create confusing code?
My advice do use it when appropriate, but use it "with" care.
First thing to do after installing Delphi:
turn on auto-save (Tools/options/Environment Options/Autosave options)
You first have to lose hours of work before you realise that at some point in the 90's an imbecile at Borland thought it was a good idea to leave this off by default.
While you're in the options dialog, change the file backup limit from 10 to 90 (it's the maximum). I find the default 10 not to be enough, unless I'm submitting buggy code into the versioning system all the time. Disk space is cheap.
Do destroy a list of object in reverse order:
for i := aList.Count -1 downto 0 do aList[i].free;
Do use regular expressions for validating complex string input values. There's a great TPerlRegEx component. I highly recommend RegexBuddy which generates TPerlRegex code automatically.
Use subversion for version control and synchronizing between multiple computers (laptop/desktop).
Make InnoSetup project with all your current projects and third party components to recover quickly from HDD failure.
Never buy a component without source code.
Instead of buying Delphi Architect or Enterprise, buy a Professional edition and treat yourself with DevExpress VCL subscription and REMObjects. (This only applies if you don't need what Enterprise and Architect editions offer. I don't want to suggest that Delphi is expensive, because with all the functionality that you get with it, native code, large community and all, it's not, just the opposite. I'm switching to the Enterprise from the Professional this month.)
Use conditional breakpoints to speed up your debugging and testing.
Always use FastMM FullDebug Mode while developing your application too look for possible memory leaks as you add new functionality.
Always use exception catching tool such as EurekaLog (my choice) or MadExcept, while developing. I even deploy my applications with it and integrate it with my FogBugz account.
Use a code profiler. AQtime if you can afford it. I really like ProDelphi. Inexpensive but alters your code. If you choose it just backup your project, profile and restore. It's very precise.
Don't use "inherited;"
It can cause subtle glitches if there isn't actually a method with the same signature in an ancestor class. It's better to state your intentions explicitly: inherited Create(AParameter);
(And yes, some people say it's good to use the naked inherited command because in certain situations involving abstract methods it can get you into trouble. But as long as we're explicitly stating our intentions here, the best solution is to just not put an inherited call in those methods. Perhaps you could even add a comment: "//do not call inherited here because..."
DO: Unit test. Take some time to become familiar with DUnit, and use it as much as you can. Not only will it make your code better, but you'll start designing your code in a better way in order to facilitate testing.
Do use proper names for variables which by looking at them one can easily tell what its for. If there would be any questions, then a small comment goes along way to saving an enormous amount of time later.
DON'T: write a Method with 100's of lines of code...with multiple levels of Boolean logic.
DON'T: USE WITH's...another person on the band wagon...DON'T USE WITH's
DO: Refactor...Take those long methods and break them up. Use Extract Method Shift+Ctrl+ M. This is another reason to not use With...as you can't Refactor a Method when it is in a With Statement.
DO: Use Sets...instead of lots of boolean variables. Which is easier to read
if (FStatus * cAllConnectErrors) = cNoErrors//[] then
or
if not (msConnectionProblem or msSendHeaderProblem or msUnknownError) then
DON'T: Swallow Exceptions and/or then show a less descriptive message. In turn eating what really happened. I really do want to know that I have a permission issue with that Update Query...not just the name of the query...
DO: use Environment Variables Tools/Options/Environment Variables...
DPROJ files have a tendency of getting corrupted and out of sync with the DPR. One way to easily locate non-existent file references within the DPROJ is to use the Class Browser from GExperts. It'll give you an error dialog for every non-existent file reference it finds. Copy-paste these into Notepad and when it's done you have a handy list of what needs to be cleaned out.
(Thanks to Skamradt [1] for showing me this trick.)
[1] http://stackoverflow.com/users/9217/skamradtBe careful with abstractions and refactoring and, as Marco put it, don't make a religion out of it. Higher abstraction levels make code easier to write, but that's usually at the price of making it harder to debug, since you have more layers to dig through to get at what's actually happening. Use extra abstraction when it makes sense, not just whenever you can.
i might add this tip : don't "eat" exceptions like
try
SomeRoutineThatSometimesCausesAHardToFindAccessViolation
except
end;
reference : Exception Handling for Fun and Profit [1]
[1] http://conferences.embarcadero.com/article/32156When call methods with var or out parameters, include a comment before the parameter. This make your code more readable.
Ex:
obj.method(param1, {var}param2);
When you destroy an object, call FreeAndNil, except on destructors(it's not necessary).
DON'T use Data Modules, DO use a form instead and make it invisible after debugging.