Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Generics в Delphi XE2


Автор: omickron 28.12.2012, 16:13
Пытаюсь разобраться с дженериками в Delphi XE2.

Не понимаю, чем отличается 
TMyClass<T> = class(TObject)
от
TMyClass<T: class> = class(TObject)

Хочу сделать следующее:

Код

TFactory<?> = class(TObject)
private
  FList: TDictionary<stirng, T>;
public
  function GetInstance: TMyClass<T>
  //Добавляет тип в словарь
  class procedure RegisterType(AType: T); 
  //Создаёт объект хранимого типа и возвращает его.
  function CreateTypeInstance(AType: string): T;

function TFactory<?>.CreateTypeInstance(AType: string): T;
begin
  Result := FList[AType].Create;
end;



Порядок использования:
Код

TMyFactory = class(TFactory<TMyClass>)
end;

//В коде:
TMyClass1 = class(TMyClass);
TMyClass2 = class(TMyClass);
TMyFactory.RegisterType(TMyClass1);
TMyFactory.RegisterType(TMyClass2);

var Value: TMyClass;
Value := TMyFactory.GetInstance.CreateTypeInstance('TMyClass1');
try
 //Используем Value
finally
  Value.Free;
end;


Суть в том, чтобы перед началом работы зарегистрировать в фабрике всех наследников TMyClass, а потом создавать их, зная только имя.
Но не пойму, чем отличается TMyClass<T> от TMyClass<T: class>
Если использовать второй вариант, то ругается здесь:
TMyFactory.RegisterType(TMyClass1); //[DCC Error] Incompatible types: 'TMyClass' and 'class of TMyClass1'
Т.е. хочет, чтоб я передал TMyClass1.Create. А я не хочу создавать объект сейчас. Я хочу просто зарегистрировать тип.
Как нужно описать тип TFactory?

Автор: Illusion Dolphin 28.12.2012, 18:38
Цитата

Не понимаю, чем отличается 
TMyClass<T> = class(TObject)
от
TMyClass<T: class> = class(TObject)

В первом случае T - любой тип, во втором T должен быть классом.

TFactory<TMyClass> - значит что T это объекты типа TMyClass. Чтобы туда добавлять типы объектов, надо сделать:
Код

type
  TMyClassType = class of TMyClass;

TMyFactory = class(TFactory<TMyClassType>)
end;

Автор: bems 28.12.2012, 20:48
не очень понятно зачем тебе тут дженерики. с этой задачей метаклассы отлично справлялись задолго до них. разве что для того чтобы возвращать тип, который уже не нужно приводить

Автор: omickron 29.12.2012, 23:44
bems, Вы совершенно правы. У меня уже есть функционал, который работает, но приходится делать приведение типов.
Я перешёл на XE2 и хочу переписать это с использованием дженериков. Если это, конечно, возможно

Illusion Dolphin, скажите, а что означает такая конструкция:
TMyClass<T: constructor> = class(TObject)
Если я правильно понимаю, означает ли это, что тип, передаваемый в T, должен иметь конструктор без параметров? И тогда можно будет в коде написать что-то вроде Result := T.Create; ?
Как бы указать в параметризации, что я хочу параметризовать класс не объектом, а его типом: не TMyClass, а class of TMyClass.
Если я укажу TFactory<T: class>, это будет означать, что я должен параметризовать его типом TMyClass. А мне нужно параметризовать его типом class of TMyClass.

Автор: Illusion Dolphin 30.12.2012, 10:16
Цитата

Если я правильно понимаю, означает ли это, что тип, передаваемый в T, должен иметь конструктор без параметров? И тогда можно будет в коде написать что-то вроде Result := T.Create; ?

Точно так:

Код

  public
    { Public declarations }
    function CreateClass<T: constructor>: T;


function TForm28.CreateClass<T>: T;
begin
  Result := T.Create;
end;


Вот пример реализации того что в первом посте:
Код

unit Unit28;

interface

uses
  Generics.Collections,
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TMyClass = class
  end;

  TMyClassType = class of TMyClass;

  TMyClassA = class(TMyClass)
  end;

  TMyClassB = class(TMyClass)
  end;

  TMyClassFactory = class
  private
    FList: TDictionary<string, TMyClassType>;
  public
    constructor Create;
    destructor Destroy; override;
    procedure RegisterClass(ClassType: TMyClassType);
    function CreateInstance(Name: string): TMyClass;
  end;

  TForm28 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form28: TForm28;

implementation

{$R *.dfm}

procedure TForm28.FormCreate(Sender: TObject);
var
  Factory: TMyClassFactory;
  C: TMyClass;
begin
  Factory := TMyClassFactory.Create;
  try
    Factory.RegisterClass(TMyClassA);
    Factory.RegisterClass(TMyClassB);

    C := Factory.CreateInstance('TMyClassB');
    ShowMessage(C.ClassName);
    C.Free;
  finally
    Factory.Free;
  end;
end;

{ TMyClassFactory }

constructor TMyClassFactory.Create;
begin
  FList := TDictionary<string, TMyClassType>.Create;
end;

function TMyClassFactory.CreateInstance(Name: string): TMyClass;
begin
  Result := FList[Name].Create;
end;

destructor TMyClassFactory.Destroy;
begin
  FList.Free;
  inherited;
end;

procedure TMyClassFactory.RegisterClass(ClassType: TMyClassType);
begin
  FList.Add(ClassType.ClassName, ClassType);
end;

end.



P.S. Практического смысла тут не вижу.

Автор: omickron 30.12.2012, 10:49
Illusion Dolphin, смысл в том, чтобы сделать общую фабрику, которая будет оперировать типами классов (class of TMyClass). TFactory<T> = class(TObject).
А потом TMyClassType = class of TMyClass и TMyFactory = class(TFactory<TMyClassType>). И TMyFactory будет отдавать зарегистрированные типы по имени.

Вот есть общая фабрика TFactory<T> и у неё есть метод GetElement(Name: string): T;
и её наследник: TMyClassFactory = class(TFactory<TMyClass>)
Что будет возвращать GetElement? TMyClass или TMyClass.Create? Созданный объект или тип объекта?

* Если указать TFactory<T>, T - любой тип. 
* Если указать TFactory<T: class>, в T можно передавать только классы.
* Если указать TFactory<T: class, constructor>, в T можно передавать классы, у которых есть конструктор без параметров.
Что во всех этих случаях будет возвращать GetElement: T? 

И что нужно указать вместе с T, чтобы передавать не TMyClass, а TMyClassType?

Автор: Illusion Dolphin 30.12.2012, 11:00
при:
Цитата

Вот есть общая фабрика TFactory<T> и у неё есть метод GetElement(Name: string): T;
и её наследник: TMyClassFactory = class(TFactory<TMyClass>)
Что будет возвращать GetElement? TMyClass или TMyClass.Create? Созданный объект или тип объекта?

GetElement возвращает объекты типа TMyClass (т.е. созданные объекты)

Цитата

И что нужно указать вместе с T, чтобы передавать не TMyClass, а TMyClassType? 

Код

 TMyClassFactory = class(TFactory<TMyClassType>)

? Я не понимаю, в чём проблема. 

Автор: omickron 30.12.2012, 11:06
Код

TMyClassFactory = class(TFactory<TMyClassType>)

А как объявить TFactory<?>, чтоб эта общая фабрика знала, что она будет параметризована типом объекта? Например, чтоб можно было сделать такой метод:
Код

procedure TFactory<?>.RegisterClass(AClass: T);
begin
  FList.Add(T.ClassName, T); //Здесь FList - словарь из примера выше.
end;


Осталось понять только это smile

Автор: Illusion Dolphin 30.12.2012, 11:42
Если я правильно понял, то тут RTTI надо:
Код

unit Unit28;

interface

uses
  Generics.Collections,
  System.TypInfo,
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TMyClass = class
  end;

  TMyClassA = class(TMyClass)
  end;

  TMyClassB = class(TMyClass)
  end;

  TMyClassFactory<T: class, constructor> = class
  private
    FList: TDictionary<string, TClass>;
  public
    constructor Create;
    destructor Destroy; override;
    procedure RegisterClass<T>();
    function CreateInstance(Name: string): T;
  end;

  TForm28 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form28: TForm28;

implementation

{$R *.dfm}

procedure TForm28.FormCreate(Sender: TObject);
var
  Factory: TMyClassFactory<TMyClass>;
  C: TMyClass;
begin
  Factory := TMyClassFactory<TMyClass>.Create;
  try
    Factory.RegisterClass<TMyClassA>();
    Factory.RegisterClass<TMyClassB>();

    C := Factory.CreateInstance('TMyClassB');
    ShowMessage(C.ClassName);
    C.Free;
  finally
    Factory.Free;
  end;
end;

{ TMyClassFactory<T> }

constructor TMyClassFactory<T>.Create;
begin
  FList := TDictionary<string, TClass>.Create;
end;

function TMyClassFactory<T>.CreateInstance(Name: string): T;
begin
  Result := T(FList[Name].Create);
end;

destructor TMyClassFactory<T>.Destroy;
begin
  FList.Free;
  inherited;
end;

procedure TMyClassFactory<T>.RegisterClass<T>;
var
  TI: PTypeInfo;
begin
  TI := PTypeInfo(TypeInfo(T));
  FList.Add(TI.Name, GetTypeData(TI).ClassType);
end;

end.


P.S. Сделал хранилище типом TDictionary<string, TClass>

Автор: omickron 30.12.2012, 11:44
Ага. Спасибо. Изучу этот аспект. Возможно, то, что надо smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)