Почему переменные объявляются как TString и создаются как TStringList?

Почему переменные объявляются как TStrings, а создаются как TStringList?

например: переменная sl объявлена ​​как TStrings, но создана как 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;

person Fabio Vitale    schedule 21.02.2012    source источник
comment
Моя главная причина: TStrings требует меньшего набора текста :)   -  person mjn    schedule 21.02.2012
comment
@mjn Почему бы не пойти до конца и не добавить TSL = TStringList во включаемый файл, который вы включаете в каждый модуль ...... ;-)   -  person David Heffernan    schedule 21.02.2012
comment
Очень хороший вопрос!! Я считаю, что происхождение этого вопроса лежит в исходном коде VCL, обычно использующем тип корневого предка для переменных, таких как TControl. Кодеры (в том числе и я) стремятся укоренить каждую переменную, но это не имеет значения. Спасибо, что заставил меня задуматься об этом.   -  person NGLN    schedule 21.02.2012
comment
Я думаю, что неплохо всегда использовать наименее конкретный тип. Это предотвращает ненужные зависимости. Так что, если это не вредит, почему бы не придерживаться этого правила и не сделать это даже в этом случае?   -  person jpfollenius    schedule 21.02.2012
comment
@smasher каких зависимостей можно избежать в рассматриваемом коде. Я вижу TStringList в теле функции.   -  person David Heffernan    schedule 22.02.2012
comment
@DavidHeffernan Моя точка зрения заключалась в том, что если вы сделаете общим правилом всегда определять типы как можно менее конкретными и будете придерживаться его, даже если это не приносит никакой пользы (как в этом случае), это нормально, с моей точки зрения, и это может быть причина, почему это делается так иногда.   -  person jpfollenius    schedule 22.02.2012
comment
@smasher На мой взгляд и опыт, выполнение в соответствии с рассматриваемым кодом мешает вам. Представляет собой ментальный блок. Вам всегда приходится думать, могу ли я использовать метод определенного типа, недоступный в более общем типе? На каком уровне иерархии мне нужно объявить переменную? Используя ваш подход, вы удаляете все методы конкретного типа из анализа кода. Когда техника не приносит пользы и вместо этого мешает вам, лучше избегать этой техники.   -  person David Heffernan    schedule 22.02.2012
comment
@ Дэвид - я склонен согласиться. Если вы собираетесь вызывать TStringList.Create, то зачем объявлять что-то кроме TStringList? Я думаю, что если вам нужно будет уменьшить его до TStrings в какой-то момент позже, вам, вероятно, следует явно указать его. Может быть, бывают моменты, когда это менее эффективно?   -  person J...    schedule 22.02.2012
comment
Еще до того, как я продолжу чтение, я почти уверен, что TStrings имеет важное значение для его способности быть опубликованным свойством любого компонента (например, дважды щелкните Lines для редактирования).   -  person Jerry Dodge    schedule 22.02.2012


Ответы (4)


На мой взгляд, это довольно бессмысленно, хотя и совершенно безвредно. Вы вполне можете объявить sl TStringList, и я всегда буду делать это именно так. Для читателя кода это упрощает понимание списка локальных переменных.

В этом коде sl всегда назначается экземпляр TStringList, поэтому нет никакой выгоды от объявления sl типом базового класса TStrings. Однако если у вас есть код, который присваивает переменной различные типы потомков TStrings, то имеет смысл объявить ее как TStrings.

Ситуации, когда вы можете объявить переменную типа TStrings, обычно возникают, когда код явно не создает экземпляр. Например, служебный метод, который получает список строк в качестве параметра, был бы более полезен, если бы он принимал TStrings, поскольку тогда ему мог быть передан любой потомок. Вот простой пример:

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

Ясно, что это гораздо полезнее, когда параметр объявлен равным TStrings, а не TStringList.

Однако код в вопросе не имеет такого характера, и я считаю, что он был бы немного улучшен, если бы sl был объявлен как тип TStringList.

person David Heffernan    schedule 21.02.2012
comment
Как вы указали (но не слишком хорошо описали), причина, по которой это хороший дизайн, заключается в том, что он позволяет вам использовать любого потомка TStrings в PrintToStdOut, поэтому TStringList, Memo1.Lines, ListBox1.Items и т. д. работают отлично. Если объявить, что он принимает TStringList, это будет означать, что последние два вызова завершатся ошибкой. - person Ken White; 21.02.2012

TStrings — это абстрактный тип, в котором реализованы не все методы.

TStringList является потомком TStrings и реализует все функции. В своем коде вы также можете объявить свою переменную как TStringList.

Однако, например. в определениях функций имеет смысл принимать параметр TStrings вместо TStringList:

procedure doSomething(lst: TStrings);

Это позволяет функции работать со всеми реализациями TStrings, а не только TStringList.

person Chris    schedule 21.02.2012

Потому что таким образом вы могли бы поместить еще одного потомка TStrings в переменную SL (я, вероятно, назвал бы это Strings, а не SL).

В вашем случае это спорно, поскольку логика вокруг SL содержит создание TStringList и отсутствие внешнего присваивания или разбора параметров.

Но если вы когда-нибудь отделите логику от присваивания, вы можете извлечь выгоду из использования любого потомка TStrings.

Например, TMemoy.Lines, TListBox.Items, TComboBox.Items и т. д.
Со стороны это выглядит как TStrings, но внутри они используют не TStringList, а своего собственного потомка.

Несколько примеров классов, происходящих от 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)
person Jeroen Wiert Pluimers    schedule 21.02.2012

TStringList — это конкретная реализация абстрактного класса TStrings.

person Petesh    schedule 21.02.2012
comment
Это правда, но это не отвечает на вопрос. - person jpfollenius; 21.02.2012
comment
Так и есть, если вы знаете, почему хорошо иметь абстрактные базовые классы, по той же причине иногда полезно иметь интерфейс. В delphi абстрактные базовые классы представляют собой интерфейсы с одним наследованием без подсчета ссылок. - person Warren P; 22.02.2012