42

Why variables are declared as TStrings and created as TStringList?

eg: the var sl is declared as TStrings but created as TStringList

var
  sl : TStrings;
begin
  sl := TStringList.Create;

  // add string values...
  sl.Add( 'Delphi' );
  sl.Add( '2.01' );

  // get string value using its index
  // sl.Strings( 0 ) will return
  //   'Delphi'
  MessageDlg(
    sl.Strings[ 0 ],
    mtInformation, [mbOk], 0 );

  sl.Free;
end;
bluish
  • 23,093
  • 23
  • 110
  • 171
Fabio Vitale
  • 2,167
  • 3
  • 24
  • 34
  • 9
    My top reason: TStrings involves less typing :) – mjn Feb 21 '12 at 15:50
  • 2
    @mjn Why not go all the way and add `TSL = TStringList` to an include file that you include in every unit....... ;-) – David Heffernan Feb 21 '12 at 16:06
  • 1
    Very good question!! I believe that the origin of this question lies in the VCL source code commonly using the root ancestor type for variables, like `TControl`. Coders (incl. me) tend to root every variable, but it has no significance. Thanks for getting me think about it. – NGLN Feb 21 '12 at 18:06
  • 5
    I think it's not a bad idea to always use the least specific type possible. That prevents unnecessary dependencies. So, if it does no harm, why not stick to this rule and do it even in this case? – jpfollenius Feb 21 '12 at 18:37
  • @smasher what dependencies can be avoided in the code in question. I can see TStringList in the body of the function. – David Heffernan Feb 21 '12 at 22:54
  • @DavidHeffernan My point was that if you make it a general rule to always define types the least specific possible and you stick to it even if it brings no benefit (as in this case) that's okay from my point of view and it could be a reason why it's done like this sometimes. – jpfollenius Feb 22 '12 at 07:42
  • @smasher In my view, and experience, doing as per code in question hinders you. It presents a mental block. You are always having to think, could I use a specific type's method not available in the more generic type? What level in the hierarchy do I need to declare the var at? By using your approach you remove all specific type's methods from code insight. When a technique has no benefits and instead hinders you, it is best to shun that technique. – David Heffernan Feb 22 '12 at 07:45
  • @David - I tend to agree. If you're going to call `TStringList.Create` then why declare as anything but a TStringList? My feeling would be that if you needed to reduce it to a `TStrings` at some point later then you should probably cast it explicitly. Maybe there are times when this is less efficient? – J... Feb 22 '12 at 13:29
  • Before I even continue reading, I'm pretty sure that `TStrings` is significant for its ability to be a published property of any component (double-click `Lines` for example to edit). – Jerry Dodge Feb 22 '12 at 17:04

4 Answers4

39

TStrings is an abstract type that doesn't have all methods implemented.

TStringList is a descendant of TStrings and implements all functions. In your code, you could declare your variable also as TStringList.

However e.g. on function definitions it makes sense to accept a TStrings parameter instead of a TStringList:

procedure doSomething(lst: TStrings);

This enables the function to work with all implementations of TStrings, not only TStringList.

Chris
  • 3,008
  • 24
  • 33
32

To my mind that is rather pointless albeit completely harmless. You could perfectly well declare sl to be TStringList and I would always do it that way. For a reader of the code it makes the list of local variables easier to understand.

In this code sl is always assigned a TStringList instance and so there's nothing to be gained from declaring sl to have the base class type of TStrings. However, if you had code that assigned a variety of different types of TStrings descendants to the variable, then it would make sense to declare it as TStrings.

The situations when you might declare a variable to be of type TStrings would typically be when the code was not explicitly creating the instance. For example a utility method that received a string list as a parameter would be more useful if it accepted a TStrings since then any descendant could be passed to it. Here's a simple example:

procedure PrintToStdOut(Strings: TStrings);
var
  Item: string;
begin
  for Item in Strings do
    Writeln(Item);
end;

Clearly this is of much greater utility when the parameter is declared to be TStrings rather than TStringList.

However, the code in the question is not of this nature and I believe that it would be ever so mildly improved if sl was declared to be of type TStringList.

David Heffernan
  • 572,264
  • 40
  • 974
  • 1,389
  • 14
    As you indicated (but didn't describe too well), the reason this is good design is that it allows you to use any `TStrings` descendant in `PrintToStdOut`, so `TStringList`, `Memo1.Lines`, `ListBox1.Items`, etc. work perfectly well. Declaring it to take a `TStringList` would mean that the last two calls would fail. – Ken White Feb 21 '12 at 17:23
10

Because that way you could put another TStrings descendant in the SL variable (I'd probably call that Strings, not SL).

In your case, that is moot, since the logic around SL contains the creation of a TStringList and no external assignment or parameter parsing.

But if you ever split the logic away from the assignment, then you could benefit from using any TStrings descendant.

For instance, a TMemoy.Lines, TListBox.Items, TComboBox.Items, etc.
From the outside it looks like they are TStrings, but internally they do not use a TStringList but their own descendant.

A few examples of classes that descend from TStrings:

source\DUnit\Contrib\DUnitWizard\Source\DelphiExperts\Common\XP_OTAEditorUtils.pas:
     TXPEditorStrings = class(TStrings)
source\fmx\FMX.ListBox.pas:
       TListBoxStrings = class(TStrings)
source\fmx\FMX.Memo.pas:
     TMemoLines = class(TStrings)
source\rtl\common\System.Classes.pas:
     TStringList = class(TStrings)
source\vcl\Vcl.ComCtrls.pas:
     TTabStrings = class(TStrings)
     TTreeStrings = class(TStrings)
     TRichEditStrings = class(TStrings)
source\vcl\Vcl.ExtCtrls.pas:
     TPageAccess = class(TStrings)
     THeaderStrings = class(TStrings)
source\vcl\Vcl.Grids.pas:
     TStringGridStrings = class(TStrings)
     TStringSparseList = class(TStrings)
source\vcl\Vcl.Outline.pas:
     TOutlineStrings = class(TStrings)
source\vcl\Vcl.StdCtrls.pas:
     TCustomComboBoxStrings = class(TStrings)
     TMemoStrings = class(TStrings)
     TListBoxStrings = class(TStrings)
source\vcl\Vcl.TabNotBk.pas:
     TTabPageAccess = class(TStrings)
Jeroen Wiert Pluimers
  • 23,000
  • 6
  • 63
  • 142
7

a TStringList is a concrete implementation of the abstract TStrings class

Petesh
  • 82,900
  • 3
  • 91
  • 111
  • That's true, but it does not answer the question. – jpfollenius Feb 21 '12 at 18:38
  • 3
    It kind of does, if you know why it's good to have Abstract base classes, the same reason it's a good idea sometimes to have an Interface. In delphi, Abstract base classes are single-inheritance interfaces without reference counting. – Warren P Feb 22 '12 at 02:58