开发者

How can I pass a group of objects to a function for creation?

So I'm working in Delphi 2007 and I am cleaning up my code. I have come to notice that in a great many procedures I declare a number of different variables of开发者_运维知识库 the same type.

for example the one procedure I am looking at now I declare 4 different string lists and I have to type var1 := TStringList.Create for each one.

I had the idea to make a procedure that took in an open array of variables, my list of 4 variables and then create them all. The call would be something like this

CreateStringLists([var1,var2,var3,var4]);

But as to my knowledge you cannot pass the open array by reference and therefore not do what I was hoping to. Does anyone have any interesting ideas about this?


Often in refactoring you need to take a very wide view of the code. Why "cleanup" a couple of operations like this, when most likely you shouldn't be doing any of these operations at all?

In this case, it seems suspicous to me that you have one routine that needs to deal with 4 separate string lists. That doesn't seem very likely to have good cohesion. Perhaps instead it should be one string list-handling routine called four times. So I'd really like to see the entire routine, rather than comment on how to make this one nit in it prettier.


You can do anything (or nearly anything) with Delphi. I don't recommend the following code to use, just to know that the trick is possible:

type
  PStringList = ^TStringList;

procedure CreateStringLists(const SL: array of PStringList);
var
  I: Integer;

begin
  for I:= 0 to High(SL) do begin
    SL[I]^:= TStringList.Create;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  SL1, SL2, SL3: TStringList;

begin
  CreateStringLists([@SL1, @SL2, @SL3]);
  SL3.Add('123');
  Caption:= SL3[0];
  SL1.Free;
  SL2.Free;
  SL3.Free;
end;


Actually, what's the problem with 4 constructors?


If it makes sense in your context, you can aggregate declarations inside a specialized TObjectList.

type
  TMyList<T:class,constructor> = class(TObjectList<T>)
  public
    procedure CreateItems(const ACount : integer);
  end;

procedure TMyList<T>.CreateItems(const ACount: integer);
var
  Index: Integer;
begin
  for Index := 0 to (ACount - 1) do Add(T.Create);
end;

// Test procedure
procedure TestMe;
var
  MyStringsList : TMyList<TStringList>;
begin
  MyStringsList := TMyList<TStringList>.Create(True);
  MyStringsList.CreateItems(10);
  // ...
  FreeAndNil(MyStringsList);
end;

So you can specialized your list.


You could create a series of overloaded versions with 2, 3, 4 etc. parameters. For example:

procedure CreateStringLists(var L1, L2: TStringList); overload;
procedure CreateStringLists(var L1, L2, L3: TStringList); overload;
procedure CreateStringLists(var L1, L2, L3, L4: TStringList); overload;

procedure CreateStringLists(var L1, L2: TStringList);
begin
  L1 := nil;
  L2 := nil;
  Try
    L1 := TStringList.Create;
    L2 := TStringList.Create;
  Except
    FreeAndNil(L2);
    FreeAndNil(L1);
    raise;
  End;
end;

// etc.

If I were doing this, I'd write a script to generate the code.

As an aside, in my own code, I would write InitialiseNil(L1, L2) at the start of that function, and FreeAndNil(L2, L1) in the exception handler. InitialiseNil and FreeAndNil are functions generated by a very simple Python script that is included in the codebase as a comment so that it can be re-run. A routine like CreareStringLists as defined above is only useful if you have a matching routine to free them all in one shot. This allows you to write:

CreateStringLists(L1, L2);
Try
  // do stuff with L1, L2
Finally
  FreeAndNil(L2, L1);
End;

Finally, I'm not saying that I would necessarily do this, but this is meant as a naive and direct answer to the question. As @T.E.D. states, the need to do this suggests deeper problems in the codebase.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜