To use normal OOP programming, you should always use the class view . You will have the most powerful object model in Delphi, including the interface and generics (in later versions of Delphi).
1. Entries, pointers and objects
Records can be evil (slow blind copy, if you forget to declare the parameter as const , write a hidden slow cleaning code, fillchar will make any line in the record a memory loss ...), but they are sometimes very convenient for accessing the binary structure (for example, some "small value") using a pointer.
A dynamic array of tiny records (for example, with one integer and one double field) will be much faster than a TList small classes; with our TDynArray wrapper , you will get access to records at a high level, with serialization, sorting, hashing, etc.
If you use pointers, you should know what you are doing. It is definitely preferable to stick with classes and TPersistent if you want to use the magic "VCL component ownership model".
Inheritance is not allowed for records. You need to either use the "variant entry" (using the case keyword in the type definition) or use nested entries. When using the C-like API, you sometimes have to use object-oriented structures. The use of nested records or variant records IMHO is much less clear than a good old model of object inheritance.
2. When to use the object
But there are places where objects are a good way to access existing data.
Even the object model is better than the new recording model, because it handles simple inheritance.
In a blog post last summer , I added some features for using objects:
A memory mapping file that I want to analyze very quickly: a pointer to such an object is simply large, and you still have methods; I use this for TFileHeader or TFileInfo that display the .zip header in SynZip.pas; A.
The Win32 structure, defined by an API call, in which I put convenient methods for easy access to data (for this you can use a record, but if the structure has some orientation of objects, which is very common, you will have to write records, which is not very convenient );
The temporary structure defined on the stack is just used during the procedure: I use this for TZStream in SynZip.pas or for our RTTI-related classes that display the Delphi generated RTTI in an object-oriented way, and not like TypeInfo, which is a function / procedure . By directly matching the contents of the RTTI memory, our code is faster than using the new RTTI classes created on the heap. We are not creating any memory that is good for its speed for an ORM platform such as ours. We need a lot of RTTI information, but we need it quickly, we need it directly.
3. How is the implementation of the object implemented in modern Delphi
The fact that the object is broken in modern Delphi is a shame, IMHO.
Usually, if you define an entry on the stack containing some variables counted by reference (for example, a string), it will be initialized with some magic compiler code at the initial level of the method / function:
type TObj = object Int: integer; Str: string; end; procedure Test; var O: TObj begin // here, an _InitializeRecord(@O,TypeInfo(TObj)) call is made O.Str := 'test'; (...) end; // here, a _FinalizeRecord(@O,TypeInfo(TObj)) call is made
Those _InitializeRecord and _FinalizeRecord will "cook", then "free" the O.Str variable.
Since Delphi 2010, I found out that sometimes this _InitializeRecord () was not always executed. If the record does not contain only any open fields, hidden calls are sometimes not generated by the compiler.
Just create the source again and it will ...
The only solution I found out was to use the record keyword instead of the object.
So, what does the resulting code look like:
/// used to store and retrieve Words in a sorted array // - is defined either as an object either as a record, due to a bug // in Delphi 2010 compiler (at least): this structure is not initialized // if defined as a record on the stack, but will be as an object TSortedWordArray = {$ifdef UNICODE}record{$else}object{$endif} public Values: TWordDynArray; Count: integer; /// add a value into the sorted array // - return the index of the new inserted value into the Values[] array // - return -(foundindex+1) if this value is already in the Values[] array function Add(aValue: Word): PtrInt; /// return the index if the supplied value in the Values[] array // - return -1 if not found function IndexOf(aValue: Word): PtrInt; {$ifdef HASINLINE}inline;{$endif} end;
{$ifdef UNICODE}record{$else}object{$endif} terrible ... but the code generation error did not occur, because ..
The resulting changes in the source code are not huge, but a little disappointing. I found out that an older version of the IDE (for example, Delphi 6/7) cannot parse such a declaration, so the class hierarchy will be broken in the editor ...: (
Backward compatibility should include regression tests. Many Delphi users remain in this product due to existing code. Function disruption is very problematic for the future of Delphi, IMHO: if you need to rewrite a lot of code, should you just switch the project to C # or Java?