Конвертировать строки в ListBox в List<String>

Компонент TListBox содержит набор строк (строк). Как я могу получить этот набор в виде списка TList? Примеры кода, приведенные ниже, не дают желаемого результата. (Код не компилируется)

MyList  := TList<String>.Create(MyListBox);
MyList  := TList<String>.Create(MyListBox.Items);
MyList  := TList<String>.Create(MyListBox.Items.ToStringArray);

Возможно ли это сделать без использования цикла или нет? Спасибо!

2 ответа

Решение

Вы можете сделать это:

MyList := TList<string>.Create;
try
  MyList.AddRange(MyListBox.Items.ToStringArray);
  ....
finally
  MyList.Free;
end;

Если вы хотите назначить элементы в конструкторе, вам понадобится экземпляр TEnumerable<string>, Это не легко привить к TStrings снаружи. Поэтому я думаю, что приведенный выше код, вероятно, самый чистый.

Ответ Дэвида прост, если вы не возражаете против выделения дополнительного массива для временной копии копируемых строк. Если вы хотите уменьшить использование памяти, особенно если список большой, цикл лучше:

var
  MyList: TList<String>;
  I: Integer;
begin
  MyList := TList<String>.Create;
  try
    MyList.Capacity := MyListBox.Items.Count;
    for i := 0 to MyList.Capacity-1 do
      MyList.Add(MyListBox.Items[I]);
    ...
  finally
    MyList.Free;
  end;
end;

В качестве альтернативы:

var
  MyList: TList<String>;
  S: String;
begin
  MyList := TList<String>.Create;
  try
    MyList.Capacity := MyListBox.Items.Count;
    for S in MyListBox.Items do
      MyList.Add(S);
    ...
  finally
    MyList.Free;
  end;
end;

Однако, если вы не хотите выполнять цикл вручную, я бы предложил создать пользовательский перечислитель, чтобы вы могли передать TStrings данные непосредственно в TList<String> и пусть он скопирует строки для вас:

type
  TStringsEnumeratorWrapper = class(TEnumerator<String>)
  protected
    FEnum: TStringsEnumerator;
    function DoGetCurrent: String; override;
    function DoMoveNext: Boolean; override;
  public
    constructor Create(AStrings: TStrings);
    destructor Destroy; override;
  end;

constructor TStringsEnumeratorWrapper.Create(AStrings: TStrings);
begin
  inherited Create;
  FEnum := AStrings.GetEnumerator;
end;

destructor TStringsEnumeratorWrapper.Destroy;
begin
  FEnum.Free;
  inherited Destroy;
end;

function TStringsEnumeratorWrapper.DoGetCurrent: String;
begin
  Result := FEnum.Current;
end;

function TStringsEnumeratorWrapper.DoMoveNext: Boolean;
begin
  Result := FEnum.MoveNext;
end;

type
  TStringsEnumerableWrapper = class(TEnumerable<String>)
  protected
    FStrings: TStrings;
    function DoGetEnumerator: TEnumerator<T>; override;
  public
    constructor Create(AStrings: TStrings);
  end;

constructor TStringsEnumerableWrapper.Create(AStrings: TStrings);
begin
  inherited Create;
  FStrings := AStrings;
end;

function TStringsEnumerableWrapper.DoGetEnumerator: TEnumerator<T>;
begin
  Result := TStringsEnumeratorWrapper.Create(FStrings);
end;

var
  MyList: TList<String>;
  Enum: TStringsEnumerableWrapper;
begin
  MyList := TList<String>.Create;
  try
    MyList.Capacity := MyListBox.Items.Count;
    Enum := TStringsEnumerableWrapper.Create(MyListBox.Items);
    try
      MyList.AddRange(Enum);
    finally
      Enum.Free;
    end;
    ...
  finally
    MyList.Free;
  end;
end;

В качестве альтернативы:

type
  TStringsEnumeratorWrapper = class(TObject, IEnumerator<String>)
  protected
    FEnum: TStringsEnumerator;
  public
    constructor Create(AStrings: TStrings);
    destructor Destroy; override;
    function GetCurrent: String;
    function MoveNext: Boolean;
    procedure Reset;
  end;

constructor TStringsEnumeratorWrapper.Create(AStrings: TStrings);
begin
  inherited Create;
  FEnum := AStrings.GetEnumerator;
end;

destructor TStringsEnumeratorWrapper.Destroy;
begin
  FEnum.Free;
  inherited Destroy;
end;

function TStringsEnumeratorWrapper.GetCurrent: String;
begin
  Result := FEnum.Current;
end;

function TStringsEnumeratorWrapper.MoveNext: Boolean;
begin
  Result := FEnum.MoveNext;
end;

procedure TStringsEnumeratorWrapper.Reset;
begin
  //FEnum.Reset;
end;

type
  TStringsEnumerableWrapper = class(TObject, IEnumerable<String>)
  protected
    FStrings: TStrings;
  public
    constructor Create(AStrings: TStrings);
    function GetEnumerator: IEnumerator<String>;
  end;

constructor TStringsEnumerableWrapper.Create(AStrings: TStrings);
begin
  inherited Create;
  FStrings := AStrings;
end;

function TStringsEnumerableWrapper.GetEnumerator: IEnumerator<String>;
begin
  Result := TStringsEnumeratorWrapper.Create(FStrings);
end;

var
  MyList: TList<String>;
begin
  MyList := TList<String>.Create;
  try
    MyList.Capacity := MyListBox.Items.Count;
    MyList.AddRange(TStringsEnumerableWrapper.Create(MyListBox.Items));
    ...
  finally
    MyList.Free;
  end;
end;

Конечно, не так элегантно, как ответ Дэвида, но счетчики были разработаны, чтобы упростить циклический просмотр элементов списка (и, таким образом, позволили создать for..in Синтаксис цикла).

Другие вопросы по тегам