Jump to content
Mike Torrettinni

TArray vs TList with VirtualStringTree

Recommended Posts

Usually I use TArray data with VirtualStringTree, but now I started testing with TList and I got to the little issue - see the error in this simple example:

 

 // copied from VirtualTrees
 TVirtualNode = packed record
  end;

  PVirtualNode = ^TVirtualNode;

  // my record used in TArray, and now in TList
  TRecLine = record
    VirtualNode: PVirtualNode;
  end;


procedure TForm1.FormCreate(Sender: TObject);
var vArray: TArray<TRecLine>;
    vList: TList<TRecLine>;
    vNode: PVirtualNode;
begin
  // just simple code to test cmopile error
   vArray[0].VirtualNode := vNode;
   vList[0].VirtualNode  := vNode; // <- [dcc32 Error] Unit1.pas(41): E2064 Left side cannot be assigned to
end;

Why it complains here? I thought TArray and TList are quite similar containers... what can I do here?

 

The rest of the code works pretty well, replacing TArray with TList.

 

Thanks!

 

Share this post


Link to post

Even though TList<T> uses a dynamic array internally, accessing its elements is very different than accessing the elements of TArray<T>.  In your case, accessing vArray[n] is direct access to your record items, but vList[n] uses the TList<T>.Items property, which returns a copy of your record items, and you can't modify a copy inline the way you are trying to.  You would have to manually save that copy to a variable and reassign it back to the TList<T> after modifying it, eg:

procedure TForm1.FormCreate(Sender: TObject);
var
  vArray: TArray<TRecLine>;
  vList: TList<TRecLine>;
  vNode: PVirtualNode;
  vRec: TRecLine;
begin
  ...

  vArray[0].VirtualNode := vNode; // OK

  //vList[0].VirtualNode  := vNode; // NOT OK!
  vRec := vList[0];
  vRec.VirtualNode := vNode;
  vList[0] := vRec;

  ...
end;

 

Edited by Remy Lebeau
  • Thanks 1

Share this post


Link to post

OK, so any kind of assignment can't be done directly to TList<T>, always through the temporary variable? Now all the benefits of TList vs TArray (like simple .Add without SetLength) don't seem worth the hassle...

To work with VSTs, I need to manipulate data, either correctly set Level for hierarchy, or prepare text for OnGetText (for speed)... or attach some other values to works with VSTs.. I guess TArray will work for me for now.

Thanks @Remy Lebeau!

Share this post


Link to post
49 minutes ago, Mike Torrettinni said:

OK, so any kind of assignment can't be done directly to TList<T>, always through the temporary variable?

Only if you use the syntax you showed, yes.  An alternative would be to use the TList<T>.List property instead, which gives you somewhat more direct access to the internal array:

procedure TForm1.FormCreate(Sender: TObject);
var
  ...
  vList: TList<TRecLine>;
  vNode: PVirtualNode;
  ...
begin
  ...
  vList.List[0].VirtualNode := vNode;
  ...
end;

Or, use the TList<T>.PList property instead, which is as bare-bones as you can get to the internal array:

type
  TRecLine = record
    ...
  end;
  PRecLine = ^TRecLine;

procedure TForm1.FormCreate(Sender: TObject);
var
  ...
  vList: TList<TRecLine>;
  vNode: PVirtualNode;
  ...
begin
  ...
  (PRecLine(vList.PList)+0{Index}).VirtualNode := vNode;
  ...
end;

 

Quote

Now all the benefits of TList vs TArray (like simple .Add without SetLength) don't seem worth the hassle...

Do note that System.TArray<T> (not to be confused with System.Generics.Collections.TArray<T>) has supported insert and concatenation operations since XE7.  See String-Like Operations Supported on Dynamic Arrays and Dynamic Arrays in Delphi XE7.

Quote

To work with VSTs, I need to manipulate data, either correctly set Level for hierarchy, or prepare text for OnGetText (for speed)... or attach some other values to works with VSTs.. I guess TArray will work for me for now.

VST doesn't care how you store your data, only that you should store a user-defined value inside each VST node so you can get back to your data given a node when needed.

 

The best way to do that would be to store an actual TRecLine pointer in the node, where that pointer points to an item in your TList/TArray.  But, if you are going to be adding/removing items from your TList/TArray, then that gets dangerous since you will be invalidating stored pointers.

 

The next best option would be to store a TList/TArray index in each node instead, but then you have to worry about invalidating stored indexes.

 

The next best option might be to store a unique ID number in each TRecLine, and then store those IDs in each node, and look for them in your TList/TArray when needed, but then you get into performance issues having to re-iterate the TList/TArray all the time.

 

Certainly other options may exist, but what they are really depends on the design of your data and how you intend to use/manipulate it.

Edited by Remy Lebeau
  • Thanks 1

Share this post


Link to post

Exactly! I'm sticking with TArray for VST data 🙂 But I do use TList<T> for some other parts of data processing, so not all is lost with my learning time with TList<T>.

Share this post


Link to post

Eh, I just got some unexplained nil pointers and exceptions... and debugger kept hiding value of TList variables, never had so much hassle with TArray. So I converted all code from TList<T> back to TArray<T>,

I will keep TList<T> for just simple cases. Well, I tried.

Share this post


Link to post
1 hour ago, Mike Torrettinni said:

Eh, I just got some unexplained nil pointers and exceptions... and debugger kept hiding value of TList variables, never had so much hassle with TArray. So I converted all code from TList<T> back to TArray<T>,

I will keep TList<T> for just simple cases. Well, I tried.

When done right, it is simpler and cleaner to code using higher level constructs. Sounds like you are blaming the tools. 

Share this post


Link to post
17 minutes ago, David Heffernan said:

When done right, it is simpler and cleaner to code using higher level constructs. Sounds like you are blaming the tools. 

I agree! Not really blaming... I just wanted to show to anybody else who is struggling with TList<T> or any other part of Delphi language, that they aren't alone and is perfectly fine to use the tools that we are used to... not everybody can be an expert in everything. Some things we conquer faster, some slower 🙂

Share this post


Link to post

Being able to manage the lifetime of classes is not an expert skill. It's well worth learning how to do it. Likely in your case you have somewhat tangled code into which you are jamming these classes. The difficulty is not that TList is hard, or that lifetime of classes is hard, but refactoring large code with different standards is hard. 

Share this post


Link to post
On 4/4/2019 at 2:59 AM, Remy Lebeau said:

 

begin
  ...
  (PRecLine(vList.PList)+0{Index}).VirtualNode := vNode;
  ...
end;

Or:

 

{$POINTERMATH ON}
...
begin
  ...
  PRecLine(vList.PList)[0].VirtualNode := vNode; 
  ...
end;

 

Edited by Rudy Velthuis
  • Like 1
  • Thanks 1

Share this post


Link to post
On 4/5/2019 at 8:59 AM, David Heffernan said:

Being able to manage the lifetime of classes is not an expert skill. It's well worth learning how to do it.

Hmmm... Seeing a lot of code that shows that many people have big problems with it, I get the impression it is an expert skill.

 

Even many people who think they mastered it do it wrong, IME. Indiscriminate use of FreeAndNil() is one sign (code smell) of such imagined mastery.

Edited by Rudy Velthuis
  • Like 1

Share this post


Link to post
On 4/4/2019 at 2:59 AM, Remy Lebeau said:

 

Or, use the TList<T>.PList property instead, which is as bare-bones as you can get to the internal array:

 

 

Why can't I find PList property in TList<T> of Delphi 10.3.2? Where exactly is it hidden?

 

Share this post


Link to post
On 9/30/2019 at 11:24 AM, Jacek Laskowski said:

 

Why can't I find PList property in TList<T> of Delphi 10.3.2? Where exactly is it hidden?

 

It probably disappeared when they introduced TListHelper.

 

GetList returns arrayofT(FListHelper.FItems);
 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×