2013-03-27 3 views
3

настоящее время я использую общий список набранный, как это:Как создать список, который может содержать объекты разных типов?

List: TList<TPoint>; 

Я хотел бы для List, чтобы иметь возможность проводить, в качестве альтернативы, точки с тремя координатами:

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

Я бы как объявить что-то вроде этого:

List: TList<TCanBeEitherTPointOrTPoint3D>; 

Естественно, это не будет работать, но я не знаю, что будет работать!

+1

Я взял на себя смелость переписывание вопрос, чтобы выразить то, что я думаю, что вы просите , Правильно ли я понял? –

+0

Да, переписывание правильное, согласно моим исследованиям, мне нужен TObjectList для хранения разных типов данных внутри одного списка. – user1769184

ответ

8

Delphi не поддерживает списки гетерогенных типов. Вы должны быть в состоянии представить все значения значений потенциала одним типом. Один из способов сделать это путем объединения всех различных типов в единый дискриминационный союз:

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

Тогда вы можете объявить список этого типа:

var 
    List: TList<TPointUnion>; 

Вы можете добавить значения типа TPointUnion в список. Чтобы построить значение этого типа, просто назначьте поле NumDimensions, а затем назначьте соответствующее поле p2 или p3. При чтении такого значения проверьте поле NumDimensions, чтобы узнать, какое поле точки содержит допустимое значение. На практике p2 всегда безопасен в использовании, поскольку его поля перекрываются с соответствующими полями p3.

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

С Delphi 2009, 'SizeOf (TPointUnion) <= TPoint2D.InstanceSize

+0

@Rob Ах, спасибо! – NGLN

+0

Я бы не думал, что производительность и память здесь особенно важны. Я бы ожидал, что удобство для кодера - нет. 1. –

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 будет передан в качестве значения (т.е. это значения будут скопированы), а не по ссылке.

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

Смежные вопросы