• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1149
  • Last Modified:

Returning a TStringList as the result of a function?

How do I return a TStringList as the result of a function?
0
markshiffer
Asked:
markshiffer
  • 5
  • 5
  • 2
  • +3
1 Solution
 
classmateCommented:
You'd better do it this way:

procedure GetStringList(Const AStringList:TStringList);
begin
  // do your manipulations, e.g.:
  AStringList.Add ('MyString');
end;

procedure NeedStringList;
var
  sl:TStringList;
begin
  sl:=TStringList.Create;
  GetStringList(sl);
  //process your stringlist
  //and finally free it:
  sl.free;
end;
0
 
markshifferAuthor Commented:
I want to know if I can return a TStringList in a function, not pass it as a parameter to a procedure. Your approach is what I am doing now....
0
 
intheCommented:
function EnumModems : TStrings;
var
  R : TRegistry;
  s : ShortString;
  N : TStringList;
  i : integer;
  j : integer;
begin
  Result:= TStringList.Create;
  R:= TRegistry.Create;
  try
    with R do begin
      RootKey:= HKEY_LOCAL_MACHINE;
      if OpenKey('\System\CurrentControlSet\Services\Class\Modem', False) then       if HasSubKeys then begin
        N:= TStringList.Create;
        try
          GetKeyNames(N);
          for i:=0 to N.Count - 1 do begin
            OpenKey(N[i], False);
            s:= ReadString('AttachedTo');
            for j:=1 to 4 do
              if Pos(Chr(j+Ord('0')), s) > 0 then
                Break;
            Result.AddObject(ReadString('DriverDesc'),TObject(j));             CloseKey;
          end;
        finally
          N.Free;
        end;
      end;
    end;
  finally
    R.Free;
  end;
end;
0
Take Control of Web Hosting For Your Clients

As a web developer or IT admin, successfully managing multiple client accounts can be challenging. In this webinar we will look at the tools provided by Media Temple and Plesk to make managing your clients’ hosting easier.

 
markshifferAuthor Commented:
You Create your Result variable but where do you free it? Isn't there a memory leak there. From what I have learned to free a stringlist you should first clear the list then free it then set it to nil. If the function does an "auto-free" for you after the return does it really clean up all of the memory?
0
 
intheCommented:
er N.Free .. 8 lines up
and i knicked that example from the paqs ;-)
was it what you looked for?
0
 
markshifferAuthor Commented:
the Result var is what I am referring to not N ... it seems like what I'm looking for, but again the code appears to have an enormous memory like though...
0
 
intheCommented:
oh yes missed that one ;-)
when trying to free the result it gives exceptions etc unless it is used as a const parameter as classmate wrote but that not what your after..dunno how to safely free the result.
0
 
MadshiCommented:
Where is the problem?

function ReturnTStrings : TStringList;
begin
  result := TStringList.Create;
  result.Add('test');
  result.Add('test2');
end;

  with ReturnTStrings do
    try
      MessageBox(0, pchar(IntToStr(Count)), 'info', 0);
    finally Free end;

Of course that's not too nice, because the caller of the function has to free the list after using it. If you don't like this behaviour, you should look at interfaces. Because if you return an interface and not a class, Delphi automatically frees it for you.

Regards, Madshi.
0
 
intheCommented:
aah the caller has to free it  ..
very good (or im very thick :-)
0
 
simonetCommented:
Unless the caller creates and frees the object - just like Madshi outlined -, having functions returning objects is not very good practice.

It's much better to let the object be a parameter of the procedure rather than the result of a function.

Example of a nice way to do it:

procedure MyProcThatChangesObject(SL :  TStringList);
begin
  with SL do
  begin
    // (...)
  end;
end;


The caller would then be responsible for explicitly create and free the TStringList:

var
  MyStrings : TStringList;
begin
  MyString := TStringList.create;
  try
     MyProcThatChangesObject(MyStrings);
     // use MyStrings anyway you want
  finally
     MyStrings.free;
  end;
end;

If you take a close look at the VCL, there are plenty of times this technique is used instead of having a function return the object. Just to mention one, is TSession.GetAliasNames.  Plenty of others can be found.

So, wrapping up, unless you don't have a choice, do not return objects from functions, but pass the objs as parameters instead. It's much cleaner code and closer to the actual VCL standards.

Yours,

Alex


0
 
markshifferAuthor Commented:
Although I agree that with the options given, using it as a parameter of a procedure as I was already doing looks like the best way, I must mention to simonet that looking to the VCL code for programming conventions is not a good idea. The VCL source is far off from good programming practice...
0
 
markshifferAuthor Commented:
Any one know how to accept a comment as an answer? I thought that there use to be a button for that but I don't see it here....
0
 
intheCommented:
after an answer has been rejected that option goes away ,madshi should post something as answer.
0
 
synatureCommented:
I have a lot of functions that return stringlists, usually properties, actually.  It does require the caller to clean up, but I've not found it to be a serious problem.

You might also consider simply returning a string with embeded #13#10 in it.  e.g.

function x : string;
begin
  result := 'A'+#13#10+'B';
end;

Procedure NeedsAStringList(s : string);
var sl : tstringlist;
begin
  sl := tstringlist.create;
  try
    sl.text := x;
    // this will produce a two line stringlist.
   .. work with it
   finally
   sl.free;
   end;
end;

I use this extensively to send stringlists over COM connections as widestrings.  Probably not very effecient, but certainly easy to implement and understand.

HTH

Brandon  
0
 
MadshiCommented:
Well, if you want to give back a kind of object, but don't like it that the caller has to free it, you have two choices:

(1) interfaces:  (needs D3 or higher)

interface

type
  IStringList = interface
    function  ItemCount : integer;
    function  GetItem   (index : integer) : string;
    property  Items     [index : integer] : string read GetItem; default;
    procedure Add       (str   : string );
  end;

function GetStringList : IStringList;

implementation

type
  TIStringList = class (TInterfacedObject, IStringList)
  public
    FStringList : TStringList;
    constructor Create;
    destructor Destroy; override;
    function  ItemCount : integer;
    function  GetItem   (index : integer) : string;
    procedure Add       (str   : string );
  end;

function GetStringList : IStringList;
begin
  result := TIStringList.Create;
  result.Add('test1');
  result.Add('test2');
end;

constructor TIStringList.Create;
begin
  inherited;
  FStringList := TStringList.Create;
end;

destructor TIStringList.Destroy;
begin
  FStringList.Free;
  inherited;
end;

function TIStringList.ItemCount : integer;
begin
  result := FStringList.Count;
end;

function TIStringList.GetItem(index: integer) : string;
begin
  result := FStringList[index];
end;

procedure TIStringList.Add(str: string);
begin
  FStringList.Add(str);
end;

Now you can use this stuff like that:

  with GetStringList do
    MessageBox(0, pchar(IntToStr(ItemCount)), 'info', 0);

Delphi frees it for you!   :-)

(2) dynamic arrays:  (needs D4 or higher)

type TDAString = array of string;  // dynamic array of string

function GetStringList : TDAString;

Now you can use it like that:

  MessageBox(0, pchar(IntToStr(Length(GetStringList))), 'info', 0);

Or like that:

var das : TDAString;
begin
  das := GetStringList;
  MessageBox(0, pchar(IntToStr(Length(das))), 'info', 0);

Again Delphi frees it for you...

Regards, Madshi.
0

Featured Post

Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

  • 5
  • 5
  • 2
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now