2015-08-21 7 views
2

Мне нужен список полиморфных объектов (разные классы объектов, но с общим базовым классом), которые я могу «сохранить» как часть файла формы.Стойкие полиморфные списки в Delphi

TList не является постоянным, а TCollection не является полиморфным.

Я, вероятно, могу опрокинуться, но предпочитаю не изобретать велосипед. Идеи?

+0

В каком смысле 'TCollection' не полиморфный? –

+0

@DavidHeffernan: методы 'Add' и 'insert' всегда создают тот же тип TCollectionItem, конечно? – Roddy

+0

@DavidHeffernan, я говорю, что они всегда создают тип класса, который вы передаете конструктору в TCollection. У вас не может быть TCollection of TAnimals, который содержит как TDogs, так и TCats. (предупреждение: плохой пример OO!) http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/Classes_TCollection_Create.html – Roddy

ответ

3

Ни один из стандартных классов библиотеки не соответствует вашим потребностям. Вам нужно сворачивать самостоятельно или найти стороннюю библиотеку.

+0

Я думаю, что это ответ на вопрос, который вы задали. Если вы хотите получить рекомендации по библиотеке, это не в тему, поскольку я уверен, что такой высокий пользователь, как вы, хорошо знаете. –

+0

Спасибо. Я пытался избежать этого окончания в мутной воде вопросов, связанных с покупками.Я, конечно, не спрашиваю конкретно о библиотеке, но если это был ответ ... – Roddy

+0

Думаю, что да. Насколько я знаю, в стандартной библиотеке нет коллекции, которая может, из коробки, сохранять разнородные коллекции. –

0

Я не уверен, почему TCollection не может удерживать TCats и TDogs?

TAnimal = class(TCollectionItem) 
end; 

TCat = class(TAnimal) 
end; 

TDog = class(TAnimal) 
end; 

FCollection : TCollection; 
FCollection := TCollection.Create(TAnimal); 

cat : TCat 
cat := TCat.Create(FCollection); 

dog : TDog 
dog := TDag.Create(FCollection); 

var 
    i : integer; 
begin 
    for I := 0 to FCollection.Count - 1 do 
    TAnimal(FCollection.Items[i]).DoSomething; 
end; 

FCollection теперь будет держать 2 вещи, кот и собака

Или я пропускаю пункт здесь?

+0

Это то, что мне нужно, но это работает? Обычно вы добавляете элементы, используя 'FCollection.Add' – Roddy

+0

Да, вы. Рассмотрите структуру потоковой передачи. DFM-файлы. –

+0

Протестировано, оно работает – GuidoG

3

Для использования инфраструктуры потоковой передачи по умолчанию вы должны создать элемент коллекции оберток, который может хранить и создавать экземпляры объектов разных классов.

unit PolyU; 

interface 

uses 
    System.SysUtils, 
    System.Classes; 

type 
    TWrapperItem = class(TCollectionItem) 
    protected 
    FObjClassName: string; 
    FObjClass: TPersistentClass; 
    FObj: TPersistent; 
    procedure SetObjClass(Value: TPersistentClass); 
    procedure SetObjClassName(Value: string); 
    procedure SetObj(Value: TPersistent); 
    function CreateObject(OClass: TPersistentClass): Boolean; dynamic; 
    public 
    property ObjClass: TPersistentClass read FObjClass write SetObjClass; 
    published 
    // ObjClassName must be published before Obj to trigger CreateObject 
    property ObjClassName: string read FObjClassName write SetObjClassName; 
    property Obj: TPersistent read FObj write SetObj; 
    end; 

implementation 

procedure TWrapperItem.SetObjClass(Value: TPersistentClass); 
begin 
    if Value <> FObjClass then 
    begin 
     FObj := nil; 
     FObjClass := Value; 
     if Value = nil then FObjClassName := '' 
     else FObjClassName := Value.ClassName; 
     CreateObject(FObjClass); 
    end; 
end; 

procedure TWrapperItem.SetObjClassName(Value: string); 
begin 
    if Value <> FObjClassName then 
    begin 
     FObj := nil; 
     FObjClassName := Value; 
     if Value = '' then FObjClass := nil 
     else FObjClass := FindClass(Value); 
     CreateObject(FObjClass); 
    end; 
end; 

procedure TWrapperItem.SetObj(Value: TPersistent); 
begin 
    FObj := Value; 
    if Assigned(Value) then 
    begin 
     FObjClassName := Value.ClassName; 
     FObjClass := TPersistentClass(Value.ClassType); 
    end 
    else 
    begin 
     FObjClassName := ''; 
     FObjClass := nil; 
    end; 
end; 

function TWrapperItem.CreateObject(OClass: TPersistentClass): Boolean; 
begin 
    Result := false; 
    if OClass = nil then exit; 
    try 
    FreeAndNil(FObj); 
    if OClass.InheritsFrom(TCollectionItem) then FObj := TCollectionItem(TCollectionItemClass(OClass).Create(nil)) 
    else 
    if OClass.InheritsFrom(TComponent) then FObj := TComponentClass(OClass).Create(nil) 
    else 
    if OClass.InheritsFrom(TPersistent) then FObj := TPersistentClass(OClass).Create; 
    Result := true; 
    except 
    end; 
end; 

end. 

Классов, которые собираются быть обернут TWrapperItem должны быть зарегистрированы в системе потоковой передачи Delphi через RegisterClass или RegisterClasses методов.

Следующий тестовый компонент содержит базовую коллекцию, которую можно редактировать и передавать через среду IDE. Для большего контроля возможно, что вы захотите написать собственные редакторы IDE, но это основа для начала.

unit Unit1; 

interface 

uses 
    System.Classes, 
    PolyU; 

type 
    TFoo = class(TPersistent) 
    protected 
    FFoo: string; 
    published 
    property Foo: string read FFoo write FFoo; 
    end; 

    TBar = class(TPersistent) 
    protected 
    FBar: integer; 
    published 
    property Bar: integer read FBar write FBar; 
    end; 

    TTestComponent = class(TComponent) 
    protected 
    FList: TOwnedCollection; 
    public 
    constructor Create(AOwner: TComponent); override; 
    destructor Destroy; override; 
    published 
    property List: TOwnedCollection read FList write FList; 
    end; 

procedure Register; 

implementation 

procedure Register; 
begin 
    RegisterComponents('Test', [TTestComponent]); 
end; 

constructor TTestComponent.Create(AOwner: TComponent); 
begin 
    inherited; 
    FList := TOwnedCollection.Create(Self, TWrapperItem); 
end; 

destructor TTestComponent.Destroy; 
begin 
    Flist.Free; 
    inherited; 
end; 

initialization 

    RegisterClasses([TFoo, TBar]); 

finalization 

    UnRegisterClasses([TFoo, TBar]); 

end. 

Это как в потоковом TTestComponent (как часть формы) может выглядеть следующим образом:

object TestComponent1: TTestComponent 
    List = < 
     item 
     ObjClassName = 'TFoo' 
     Obj.Foo = 'abc' 
     end 
     item 
     ObjClassName = 'TBar' 
     Obj.Bar = 5 
     end> 
    Left = 288 
    Top = 16 
    end 
+0

Хотя у вас может возникнуть соблазн использовать дженерики в TWrapperItem, это не сработает, потому что [потоковая система Delphi не распознает опубликованные свойства родового типа] (http://qc.embarcadero.com/wc/qcmain.aspx?d= 103296) –

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

  • Нет связанных вопросов^_^