Why variables are declared as TStrings and created as TStringList?
Asked Answered
A

4

46

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;
Adanadana answered 21/2, 2012 at 14:51 Comment(9)
My top reason: TStrings involves less typing :)Halda
@Halda Why not go all the way and add TSL = TStringList to an include file that you include in every unit....... ;-)Parthenogenesis
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.Blindheim
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?Dishrag
@smasher what dependencies can be avoided in the code in question. I can see TStringList in the body of the function.Parthenogenesis
@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.Dishrag
@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.Parthenogenesis
@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?Lupus
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).Longhair
P
35

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.

Parthenogenesis answered 21/2, 2012 at 14:54 Comment(1)
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.Hospitium
B
42

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.

Buddhi answered 21/2, 2012 at 14:57 Comment(0)
P
35

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.

Parthenogenesis answered 21/2, 2012 at 14:54 Comment(1)
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.Hospitium
S
12

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)
Steamheated answered 21/2, 2012 at 21:23 Comment(0)
A
7

a TStringList is a concrete implementation of the abstract TStrings class

Amathist answered 21/2, 2012 at 14:53 Comment(2)
That's true, but it does not answer the question.Dishrag
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.Offal

© 2022 - 2024 — McMap. All rights reserved.