Добрый день!
Подскажите пожалуйста, как можно создать экземпляр объекта заданного типа, если тип передается обобщенным параметром?
type
TCustomDataObject = class(TObject)
...
end;
TDataObject1 = class(TCustomDataObject)
...
end;
TDataObject2 = class(TCustomDataObject)
...
end;
TMyCreatorObject<T: TMyDataObject> = class(TObject)
function CreateObject: T;
end;
...
function TMyCreatorObject<T>.CreateObject: T;
begin
Result := ................. // ??? Как создать объект типа Т, наследованного от TCustomDataObject ???
end;
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
17-12-2018 03:58
Поставил Delphi 10.3 Rio. Приведенные вами примеры кода теперь работают. Похоже разработчики продают сырой материал. Нельзя так лажать, когда выдаешь выпуск за релиз программы!
Упомянутую библиотеку AsyncCals удалось допилить в две строи кода. Остальные библиотеки нашел более новые версии, адаптированные под Rio. Даже казалось бы умершую RxLib.
Благодарю Вас за отзывчивость!
TTypeInfo = record
Kind: TTypeKind;
Name: TSymbolName;
function NameFld: TTypeInfoFieldAccessor; inline;
{TypeData: TTypeData}
function TypeData: PTypeData; inline;
end;
Я начинал с RTTI разбиратся с версии XE3. Так вот там для некоторых структур в частности для структуры TTypeData выполнялось то , что если в ней присутствует комментированное поле, то оно по факту и есть ;). Т.е. вы можете поробовать скорректировать код, считая что после Name идет поле TTypeData. Это если есть какието очень везкие причины не уходить с XE2. А так то лучше перейти на новую версию, конечно.
14-12-2018 11:55 | Комментарий к предыдущим ответам
В последней версии дженерики подправили, хотя может это и чуть раньше 10.3 было, но точно недавно. Хотя с одной стороны подправили, а с другой у меня моя библиотека которая в XE3 прекрасно компилировалась в 10.3 перестала, там причем какой то Internal Error. Там дженерик рекорд с переопоеделенными операциями и от него много типов пораждено.
Но кстати вариант с RTTI абсолютно точно будет работать в XE3. А вот первый не уверен сейчас, не могу проверить, могу проверить через пару дней только.
Дело в том, что есть очень интересные разработки, развитие которых остановилось... К примеру приведенная здесь на сайте библиотека асинхронных вызовов AsyncCalls...
14-12-2018 10:03 | Комментарий к предыдущим ответам
Версия среды:
Embarcadero® Delphi® XE2 Version 16.0.4504.48759
Скопипастил один к одному "простое" решение. Та же ошибка:
"[DCC Error] Unit7.pas(34): E2568 Can't create new instance without CONSTRUCTOR constraint in type parameter declaration"
uses RTTI, TypInfo;
{ TMyCreatorObject<T> }
class function TMyCreatorObject<T>.CreateObject(A:integer): T;
var
ctx : TRttiContext;
rt : TRttiType;
Method : TRttiMethod;
v : TValue;
Obj : TObject;
begin
ctx:=TRttiContext.Create;
rt:=ctx.GetType(TypeInfo(T));//в общем случае надо проверять что T это класс, но в данном примере можно не делать
Method:=rt.GetMethod('Create');
V:=A;
Obj:=Method.Invoke(rt.Handle^.TypeData^.ClassType,v).AsObject;
Result:=Obj as T;
end;
в качестве второго параметра в методе Invoke надо передавать список аргументов. В данном примере у конструктора один параметр типа integer его и передаем.
14-12-2018 07:40 | Вопрос к автору: запрос дополнительной информации
На самом деле в классах потомках я переопределял констркуторы вот так
{ TDataObject1 }
constructor TDataObject1.Create(A: integer);
begin
inherited;
FA:=FA+1;
end;
{ TDataObject2 }
constructor TDataObject2.Create(A: integer);
begin
inherited;
FA:=FA+2;
end;
Чтобы убедится что вызван конструктор того класса, который нужен. Для этого и приватное поле, а то в том примере что я привел сначала, оно бессмысленно :).Но сейчас проверил еще раз без конструкторов в базовых классах, все компилируется без ошибок.
14-12-2018 07:29 | Комментарий к предыдущим ответам
Такая ошибка возникает, если не определен виртуальный конструктор в базовом классе. Я этот код откомпилировал и проверил. версия 10.3, нет возможности в других проварить, но так вроде должно работать и в других версиях.
Может я не чего то не понял, ну вроде же все просто, не пойму в чем проблема создать виртуальный конструктор в базовом классе. Переопределять его то не надо без необходимости. Я имел ввиду, что если вы в потомках захотите сделать конструктор у которого будут другие параметры (например добавятся дополнительные), то только тогда уже будет сложность и надо RTTI подключать. Для конкретики вот пример. Приватное поле просто для проверки, какой конструктор вызвался.
TCustomDataObject = class(TObject)
private
FA:integer;
public
constructor Create(A:integer);virtual;
end;
TDataObject1 = class(TCustomDataObject)
end;
TDataObject2 = class(TCustomDataObject)
end;
TMyCreatorObject<T: TCustomDataObject> = class(TObject)
class function CreateObject(A:integer): T;
end;
implementation
{ TMyCreatorObject<T> }
class function TMyCreatorObject<T>.CreateObject(A:integer): T;
begin
Result:=T.Create(A);
end;
{ TCustomDataObject }
constructor TCustomDataObject.Create(A: integer);
begin
FA:=A;
end;
пример использования
var O1:TCustomDataObject;
O2:TDataObject1;
O3:TDataObject2;
begin
O1:=TMyCreatorObject<TCustomDataObject>.CreateObject(1);
O2:=TMyCreatorObject<TDataObject1>.CreateObject(2);
O3:=TMyCreatorObject<TDataObject2>.CreateObject(3);
Будет вызван нужный конструктор. Смотрим приватное поле. Вроде ответ на ваш вопрос, по крайней мере на то как вы сформулировали его.
Ну если вам в общем виде надо, то это совсем другая история. И в таком случае вот такое объявление TMyCreatorObject<T: TMyDataObject> просто лишнее. Поверте лучше в потомках сделать override конструкторы :). Иначе надо подключать RTTI , узнавать тип в рантайме и разбираться с методом Invoke струкутрой TValue и всеми другими прелестями :). Я чуть позже напишу пример.
14-12-2018 03:44 | Комментарий к предыдущим ответам
W0lt, тогда нет смысла делать обобщенный класс. Весь смысл, что бы не делать у потомков одинаковых переопределяемых конструкторов, а сосредоточиться на развитии их особенностей.
В классе TCustomDataObject объявляете виртуальный конструктор. В потомке его переопределяете если надо (override) потом просто вызываете T.Create. Правда если надумаете в потомках менять параметры конструктора, то тогда надо определять какой имеенно класс у T в рантайме.
Прошу прощения, в определении класса TMyCreatorObject<T> я не верно указал тип допустимых классов <T>. Правильно будет так:
type
TCustomDataObject = class(TObject)
...
end;
TDataObject1 = class(TCustomDataObject)
...
end;
TDataObject2 = class(TCustomDataObject)
...
end;
TMyCreatorObject<T: TCustomDataObject> = class(TObject)
function CreateObject: T;
end;
...
function TMyCreatorObject<T>.CreateObject: T;
begin
Result := ................. // ??? Как создать объект типа Т, наследованного от TCustomDataObject ???
end;
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.