28

I've been using Delphi for quite some time now, but rather than coming from a CS background I have learnt "on the job" - mostly from my Boss, and augmented by bits and pieces picked up from the web, users guides, examples, etc.

Now my boss is old school, started programming using Pascal, and hasn't necessarily kept up-to-date with the latest changes to Delphi.

Just recently I've been wondering whether one of our core techniques is "wrong".

Most of our applications interface with MySQL. In general we will create a record with a structure to store data read from the DB, and these records will be stored in a TList. Generally we will have a unit that defines the various records that we have in an application, and the functions and procedures that seed and read the records. We don't use record procedures such as outlined here

After reviewing some examples I've started wondering whether we'd be better off using classes rather than records, but I'm having difficulty finding strong guidance either way.

The sort of thing that we are dealing with would be User information: Names, DOB, Events, Event Types. Or Timesheet information: Hours, Jobs, etc...

Community
  • 1
  • 1
Dan Kelly
  • 2,634
  • 5
  • 41
  • 61

1 Answers1

46

The big difference is that records are value types and classes are reference types. In a nutshell what this means is that:

  1. For a value type, when you use assignment, a := b, a copy is made. There are two distinct instances, a and b.
  2. For a reference type, when you use assignment, a := b, both variables refer to the same instance. There is only one instance.

The main consequence of this is what happens when you write a.Field := 42. For a record, the value type, the assignment a.Field changes the value of the member in a, but not in b. That's because a and b are different instances. But for a class, since a and b both refer to the same instance, then after executing a.Field := 42 you are safe to assert that b.Field = 42.

There's no hard and fast rule that says that you should always use value types, or always use reference types. Both have their place. In some situations, it will be preferable to use one, and in other situations it will be preferable to use the other. Essentially the decision always comes down to a decision on what you want the assignment operator to mean.

You have an existing code base, and presumably programmers familiar with it, that has made particular choices. Unless you have a compelling reason to switch to using reference types, making the change will almost certainly lead to defects. And defects both in the existing code (switch to reference type changes meaning of assignment operator), and in code you write in the future (you and your colleagues have developed intuition as to meaning of assignment operator in specific contexts, and that intuition will break if you switch).

What's more, you state that your types do not use methods. A type that consists only of data, and has no methods associated with it is very likely best represented by a value type. I cannot say that for sure, but my instincts tell me that the original developers made the right choice.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • +1 Totally opposite of my first instinct but very pragmatic so in the end, I have to agree. OP should be aware though of a possible performance hit due to assignments. For instance: Passing record parameters as `const` should be the default but then again, that might break the application and we're back at trying to be pragmatic . – Lieven Keersmaekers Oct 17 '13 at 11:55
  • @LievenKeersmaekers Yes, I did think about discussing parameter passing, but opted not to for simplicity. You can think of parameter passing by value as equivalent to assignment. Parameters are local variables. Passing by value copies the value or the reference depending on the type of the parameter. So what you know for assignments applies equally to by value parameter passing. – David Heffernan Oct 17 '13 at 12:06
  • We generally use records only when streaming structured data (e.g. TCP, serial, or disk), in which case we use packed records to ensure the size remains finite. We'll use classes/objects for everything else. – Marcus Adams Oct 17 '13 at 12:39
  • @Marcus I personally am not keen on streaming like that. You build in endianness and layout assumptions. There are oodles of places where records makes sense. Look at the range of types used in the VCL that are records. What about operator overloading? – David Heffernan Oct 17 '13 at 12:55
  • Thanks for that. My gut has certainly been to leave be as it currently id - just trying to plan forwards... Record methods are attractive as far as I understand them, as I will normally have 3/4 methods associated with each record type (Load, Save, Find, etc) – Dan Kelly Oct 17 '13 at 13:03
  • 6
    @DanKelly: another difference between `records` and `classes` is memory management. When you declare a record type variable, it´s memroy is allocated by the compiler using the stack. When the variable goes off scope, its memory is managed (disposed) automatically. When you declare a class type variable, what you have is a reference and you have to invoke its constructor in order to alloocate the instance you´ll work on. When you finnished using that object, (for now) it´s your responsibility to destroy that instance by (directly or not) calling its destructor. – AlexSC Oct 17 '13 at 17:35
  • 1
    @Alex In this case they are stored in TList and so they are also heap allocated. – David Heffernan Oct 17 '13 at 17:38
  • If you create LOTS of those records, I would definitively stick with RECORD instead of CLASS. – Gabriel May 09 '14 at 13:22
  • +1 What are the odds of reading this and reading http://oreilly.com/catalog/delphi/chapter/ch02.html in the same day! – Michael Riley - AKA Gunny Jul 06 '14 at 17:45
  • Oreilly took down the link here a new one https://web.archive.org/web/20140923052246/http://oreilly.com/catalog/delphi/chapter/ch02.html – Michael Riley - AKA Gunny Mar 20 '16 at 14:09
  • @Artix Surely that is going to depend on the application in question? As records are value types, the creation of 'lots' of records could have a big impact on the space-complexity of your functions (each one being a copy in memory). At least with classes (ref types) you are only storing a pointer to a class in memory. – LintfordPickle Jul 10 '18 at 12:40