Question

TListBox component contains a set of rows (strings). How can I get this set as a list TList ? The code examples below, do not give the desired result. (Сode does not compile)

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

Is it possible to do this without using a loop or not? Thanks!

Was it helpful?

Solution

You can do this:

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

If you wanted to assign the items in the constructor you'd need an instance of TEnumerable<string>. That's not easy to graft on to TStrings from the outside. So I think the above code is probably the cleanest.

OTHER TIPS

David's answer is simple, if you do not mind an extra array being allocated to hold a temp copy of the strings being copied. If you want to reduce memory usage, especially if the list is large, a loop is better:

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;

Alternatively:

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;

However, if you don't want to loop manually, so I would suggest creating a custom enumerator so you can pass the TStrings data directly to TList<String> and let it copy the strings for you:

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;

Alternatively:

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;

Granted, not as elegant as David's answer, but enumerators were designed to help make looping through list items easier (and thus allowed the creation of the for..in loop syntax).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top