如何创建可以容纳不同类型对象的列表?

时间:2013-03-27 17:51:26

标签: delphi

我目前正在使用这样输入的通用列表:

List: TList<TPoint>;

我希望List能够作为替代方案持有三个坐标的点:

type 
  TPoint3D = record 
    x, y, z: Integer;
  end;

我想宣布这样的事情:

List: TList<TCanBeEitherTPointOrTPoint3D>;

当然这不起作用,但我不知道它会起作用!

4 个答案:

答案 0 :(得分:8)

Delphi不支持异类类型列表。您必须能够使用单一类型表示所有潜在值。一种方法是将所有不同的类型加入一个有区别的联合:

type
  TPointUnion = record
    case NumDimensions: Integer of
      2: (p2: TPoint);
      3: (p3: TPoint3D);
  end;

然后您可以声明该类型的列表:

var
  List: TList<TPointUnion>;

您可以将类型TPointUnion的值添加到列表中。要构建该类型的值,只需指定NumDimensions字段,然后指定相应的p2p3字段即可。读取此类值时,请检查NumDimensions字段以发现哪个点字段包含有效值。实际上,p2始终可以安全使用,因为其字段与p3的相应字段重叠。

答案 1 :(得分:1)

作为替代方案,您可以使用classe类型而不是record type with variant parts作为Rob suggests

type
  TPoint2D = class(TObject)
    X: Integer;
    Y: Integer;
  end;

  TPoint3D = class(TPoint2D)
    Z: Integer;
  end;

var
  List: TList<TPoint2D>;

begin
  if List[I] is TPoint2D then
    TPoint2D(List[I]).X := 10
  else
    TPoint3D(List[I]).Z := 10;

我不确切知道这种方法是否会表现更好,但内存使用情况会相同

答案 2 :(得分:0)

System.RTTI.TValue可能有所帮助:

type
  tAnyList = tList<TValue>;  // Generic TList of TValue

procedure testTAnyList;
var
  tal: tAnyList;
  pt3d: TPoint3D;
  pt2d: TPoint2D;
begin
  // Init pt3d, pt2d here
  tal := tAnyList.Create;
  try
    tal.Add('Some Text');        // Store text
    tal.Add(16);                 // Some integer
    tal.Add(form1);              // Object
    tal.Add(tValue.From(pt3d));  // TPoint3D record
    tal.Add(tValue.From(pt2d));  // TPoint2D record
  finally
    tal.Free;
  end;
end;

注意使用记录时:pt3d将作为值传递(即它的值将被复制),而不是通过引用传递。

答案 3 :(得分:0)

program MultiClass;

{$APPTYPE CONSOLE}

uses
  SysUtils, Generics.Defaults, Generics.Collections;

type

  TPoint2D = class
    x, y: integer;
    constructor create(x, y: integer);
  end;

  TPoint3D = class
    x, y, z: integer;
    constructor create(x, y, z: integer);
  end;

  TPointSelector = (ePoint2D, ePoint3D);

  TPoint2DOrTPoint3D = class
    select: TPointSelector;
    obj: Pointer;
    constructor create(myClass: TPoint2D); overload;
    constructor create(myClass: TPoint3D); overload;
    constructor create(x, y: integer); overload;
    constructor create(x, y, z: integer); overload;
    destructor Destroy; override;
  end;

Constructor TPoint2D.create(x: integer; y: integer);
begin
  self.x := x;
  self.y := y;
end;

Constructor TPoint3D.create(x: integer; y: integer; z: integer);
begin
  self.x := x;
  self.y := y;
  self.z := y;
end;

constructor TPoint2DOrTPoint3D.create(x, y: integer);
begin
  select := ePoint2D;
  obj := TPoint2D.create(x, y);
end;

constructor TPoint2DOrTPoint3D.create(x, y, z: integer);
begin
  select := ePoint3D;
  obj := TPoint3D.create(x, y, z);
end;

constructor TPoint2DOrTPoint3D.create(myClass: TPoint2D);
begin
  select := ePoint2D;
  obj := TPoint2D;
end;

constructor TPoint2DOrTPoint3D.create(myClass: TPoint3D);
begin
  select := ePoint3D;
  obj := TPoint3D;
end;

destructor TPoint2DOrTPoint3D.Destroy;
begin
  if assigned(obj) then
    case select of
      ePoint2D:
        TPoint2D(obj).Free;
      ePoint3D:
        TPoint3D(obj).Free;
    end;
end;

procedure Test;
var
  PointList: TObjectList<TPoint2DOrTPoint3D>;
  I: integer;
  Pt: TPoint2DOrTPoint3D;
  P3D: TPoint3D;
  P2D: TPoint2D;
begin
  PointList := TObjectList<TPoint2DOrTPoint3D>.create;
  PointList.OwnsObjects := true;

  PointList.Add(TPoint2DOrTPoint3D.create(10, 10));
  PointList.Add(TPoint2DOrTPoint3D.create(50, 10));
  PointList.Add(TPoint2DOrTPoint3D.create(-10, 10, 90));
  PointList.Add(TPoint2DOrTPoint3D.create(3, 50, 70));

  for Pt in PointList do
  begin
    case Pt.select of
      ePoint2D:
        begin
          P2D := Pt.obj;
          writeln(P2D.x, '  ', P2D.y);
        end;
      ePoint3D:
        begin
          P3D := Pt.obj;
          writeln(P3D.x, '  ', P3D.y, '  ', P3D.z);
        end;
    end;
  end;

  PointList.Destroy;

end;

begin
  try
    Test;
  except
    on E: Exception do
      writeln(E.ClassName, ': ', E.Message);
  end;
  readln;

end.

这可能有点矫枉过正,但我​​认为这可能是相关的

在Delphi XE中编程