Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: Extracting a value from a TList<>


This question is answered. Helpful answers available: 2. Correct answers available: 1.


Permlink Replies: 5 - Last Post: Jun 28, 2016 12:58 PM Last Post By: John Mitchell
John Mitchell

Posts: 64
Registered: 9/23/02
Extracting a value from a TList<>  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 27, 2016 8:16 PM
I'm confused about extracting a value from a generics TList.
I'm using Seattle.

type TUser = class
        usertype : string; 
        userid   : string;  
       end;
 
type TUserList = TList<TUser>;
 
Procedure FreeUserList(UserList : TUserList);
{ frees the user list }
var i : integer;
begin
 if UserList = nil then
  Exit;
 
 for i := Pred(UserList.Count) downto 0 do
    if Assigned(UserList[i]) then
      TnxUser(UserList[i]).Free;
 UserList.Clear;
 UserList.Free;
 
end;
 
{
 I need a function to return a user record from the UserList, which I need to persist, display values, put onto form, etc.
}
 
Function GetUser(PK,RK : string) : TUser;
{ I want this function to return a user, for use elsewhere }
var userList : TUserList;
    FilterS : string;
begin
  Result := nil;
  FilterS := 'PartitionKey eq ' + QuotedStr(PK) + ' and RowKey eq '  + QuotedStr(RK);
  userList := UserQuery('usertable',FilterS);      // UserQuery returns a TUserList, this works, count is 1.  Code not shown, but UserList is created here
  if userList.Count > 0 then                                // "1" as expected
    Result := UserList[0];                                    // "result" at this point contains a good user record, the usertype and name nonblank like I want
  FreeUserList(UserList);                                 // BUT when this line frees the list though, my user record (result) is now blank   (userid = '')
end;


I'm kind of new at generics, obviously.
Can anyone see where I'm going wrong here ?
Antonio Estevez

Posts: 665
Registered: 4/12/00
Re: Extracting a value from a TList<>  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 27, 2016 9:53 PM   in response to: John Mitchell in response to: John Mitchell
El 28/06/2016 a las 5:16, John Mitchell escribió:
I'm confused about extracting a value from a generics TList.
I'm using Seattle.

type TUser = class
        usertype : string;
        userid   : string;
       end;
 
type TUserList = TList<TUser>;
 
Procedure FreeUserList(UserList : TUserList);
{ frees the user list }
var i : integer;
begin
 if UserList = nil then
  Exit;
 
 for i := Pred(UserList.Count) downto 0 do
    if Assigned(UserList[i]) then
      TnxUser(UserList[i]).Free;
 UserList.Clear;
 UserList.Free;
 
end;
 
{
 I need a function to return a user record from the UserList, which I need to persist, display values, put onto form, etc.
}
 
Function GetUser(PK,RK : string) : TUser;
{ I want this function to return a user, for use elsewhere }
var userList : TUserList;
    FilterS : string;
begin
  Result := nil;
  FilterS := 'PartitionKey eq ' + QuotedStr(PK) + ' and RowKey eq '  + QuotedStr(RK);
  userList := UserQuery('usertable',FilterS);      // UserQuery returns a TUserList, this works, count is 1.  Code not shown, but UserList is created here
  if userList.Count > 0 then                                // "1" as expected
    Result := UserList[0];                                    // "result" at this point contains a good user record, the usertype and name nonblank like I want
  FreeUserList(UserList);                                 // BUT when this line frees the list though, my user record (result) is now blank   (userid = '')
end;


I'm kind of new at generics, obviously.
Can anyone see where I'm going wrong here ?

Set the list item to nil after extracting it from the list:

   if userList.Count > 0 then
   begin
     Result := userList[0];
     userList[0] := nil;
   end;
John Mitchell

Posts: 64
Registered: 9/23/02
Re: Extracting a value from a TList<>  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 28, 2016 6:31 AM   in response to: Antonio Estevez in response to: Antonio Estevez
Antonio Estevez wrote:
Set the list item to nil after extracting it from the list:

Thanks Antonio, that was exactly what I was missing.
I 'm not quite sure why that works, but it works perfectly.
Thanks !
Lajos Juhasz

Posts: 801
Registered: 3/14/14
Re: Extracting a value from a TList<>  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 28, 2016 2:06 AM   in response to: John Mitchell in response to: John Mitchell
John Mitchell wrote:

[snip]

You didn't provided enough information. However your bug has nothing to
do with generics. This would be a bug even with a plain TList.

In your case you would like to remove the TUser from the list and leave
the object alive as you're returning it as a result of the function.
Thus your code should be:

{code}
Function GetUser(PK,RK : string) : TUser;
{ I want this function to return a user, for use elsewhere }
var userList : TUserList;
FilterS : string;
begin
Result := nil;
FilterS := 'PartitionKey eq ' + QuotedStr(PK) + ' and RowKey eq ' +
QuotedStr(RK);
userList := UserQuery('usertable',FilterS); // UserQuery returns
a TUserList, this works, count is 1. Code not shown, but UserList is
created here
if userList.Count > 0 then // "1" as
expected
Result := UserList[0];
UserList.free;
end;

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: Extracting a value from a TList<>  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 28, 2016 10:49 AM   in response to: John Mitchell in response to: John Mitchell
John wrote:

type TUserList = TList<TUser>;

Since TUser is a class, you should use TObjectList<T> instead, which has
an OwnsObjects property that is true by default. Then your entire FreeUserList()
function becomes obsolete, since the TObjectList will automatically free
the objects it contains when itself is freed.

if Assigned(UserList[i]) then
TnxUser(UserList[i]).Free;

You do not need to check for Assigned() (Free() does that for you), you don't
need the type-cast (that is the whole point of using a Generics list), and
you don't need to call Clear() before Free().

Procedure FreeUserList(UserList : TUserList);
{ frees the user list }
  var i : integer;
begin
  if UserList = nil then
    Exit;
  for i := Pred(UserList.Count) downto 0 do
    UserList[i].Free;
  UserList.Free;
end;


Which then becomes this if you switch to TObjectList<T>:

Procedure FreeUserList(UserList : TUserList);
{ frees the user list }
begin
  UserList.Free;
end;


userList := UserQuery('usertable',FilterS); // UserQuery returns a
TUserList, this works, count is 1. Code not shown, but UserList is created
here
if userList.Count > 0 then // "1" as expected
Result := UserList[0]; // "result" at
this point contains a good user record, the usertype and name nonblank like
I want
FreeUserList(UserList); // BUT when this
line frees the list though, my user record (result) is now blank (userid
= '')

Right, because FreeUserList() destroys the TUser object. If you don't want
that, you can remove the object from the list first:

if userList.Count > 0 then
begin
  Result := UserList.First;
  UserList.Remove(0); // or: UserList[0] := nil;
end;
FreeUserList(UserList);


Or, if using TObjectList<T>:

if userList.Count > 0 then
begin
  Result := UserList.First;
  // don't use UserList.Delete(0) or UserList[0] := nil;
  // as both will free the object!
  UserList.Extract(Result);
end;
FreeUserList(UserList);


Alternatively:

type
  TUserList = class(TObjectList<TUser>)
  public
    function ExtractAtIndex(Index: Integer): TUser;
  end;
 
function TUserList.ExtractAtIndex(Index: Integer): TUser;
var
  OldValue: Boolean;
begin
  Result := Items[Index];
  // Delete() takes an index, but destroys the object when OwnsObjects=True.
  // Extract() and Remove() take a TUser as input instead of an Index.
  // Remove() destroys the object, Extract() does not. We want Extract()
  // semantics with an index, but TList<T>.doDelete() is private so we cannot
  // simply call doDelete(Index, cnExtracted) to avoid the object being destroyed...
  OldValue := OwnsObjects;
  OwnsObjects := False;
  try
    inherited Delete(Index);
  finally
    OwnsObjects := OldValue;
  end;
end;
 
...
 
if userList.Count > 0 then
  Result := UserList.ExtractAtIndex(0);
FreeUserList(UserList);


--
Remy Lebeau (TeamB)
John Mitchell

Posts: 64
Registered: 9/23/02
Re: Extracting a value from a TList<>  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Jun 28, 2016 12:58 PM   in response to: John Mitchell in response to: John Mitchell
Remy - that is great stuff, it really helped a lot.

I'll play around with those options.
I very much appreciate your taking the time to explain this to me.
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02