Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 Cоздать объект по типу, записанному в строке  [new]
aXS_new
Guest
Можно ли создать объект по типу, записанному в строке? Что-то вроде

obj := TClass.create();


только тип данных, к примеру, считан из файла. Понимаю, что можно перебирать через условие и создавать объекты по совпадению с определенным типом, но интересует, есть ли возможность сделать это без кучи условий.
3 окт 18, 18:43    [21694457]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
Dimitry Sibiryakov
Member

Откуда:
Сообщений: 46376

aXS_new
Понимаю, что можно перебирать через условие и создавать объекты по совпадению с
определенным типом, но интересует, есть ли возможность сделать это без кучи условий.

Нельзя. Но с RegisterClass ты можешь спрятать этот перебор под ковёр. Для большинства
современных программистов этого хватает.

Posted via ActualForum NNTP Server 1.5

3 окт 18, 18:58    [21694476]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
Мимопроходящий
Member

Откуда: бурятский тундрюк, эсквайр
Сообщений: 29296

03.10.2018 18:43, aXS_new пишет:
> Можно ли создать объект по типу, записанному в строке?

можно конечно.
но все возможные классы которые ты попытаешься создать должны быть зарегистрированы в аппликации.
почитай про Class of... и как его едят.

Posted via ActualForum NNTP Server 1.5

3 окт 18, 19:04    [21694486]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1203
Можно, так работает сериализация/десериализация
var
  s: string;
begin
  s := 'Unit1.TForm1';
  TRttiContext.Create.FindType(s).GetMethod('Create').Invoke(блабла)

Естественно надо понимать что конструкторов может быть несколько, поэтому такие дела лучше указывать через атрибуты, например какой конструктор будет использоваться при автоматическом создании, чтобы автоматически подставить параметры.
Из нюансов требуется полный путь к типу, т.е. с указанием имени модуля.
3 окт 18, 19:10    [21694500]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
aXS_new
Guest
X-Cite, вот именно десериализацию и пытаюсь сделать. Но проблема такая: есть поле типа "TList<TMyClass>", и параметр className (получаю через TRttiContext) содержит такую строку: "TList<main.TMyClass>", вот и думаю, как, во-первых, все это преобразовать в TList<TMyClass> и, во-вторых, записать в него полученные из сериализованной строки объекты класса "TMyClass".
3 окт 18, 19:59    [21694536]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
rgreat
Member

Откуда:
Сообщений: 4584
Опять люди свой компилятор пишут.

И зачем все так усложнять?
Неужели перебором из N вариантов классов сделать было нельзя?

Работать будет проще, предсказуемей и надежней.
3 окт 18, 20:02    [21694537]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1203
aXS_new
X-Cite, вот именно десериализацию и пытаюсь сделать. Но проблема такая: есть поле типа "TList<TMyClass>", и параметр className (получаю через TRttiContext) содержит такую строку: "TList<main.TMyClass>", вот и думаю, как, во-первых, все это преобразовать в TList<TMyClass> и, во-вторых, записать в него полученные из сериализованной строки объекты класса "TMyClass".

Зачем преобразовывать?
Для сериализации корректно указывать полные имена классов - через имя модуля.
Т.к. в разных модулях (читай неймспейсах) могут быть классы с одинаковым именованием. Откуда десериализатору узнать какой использовать без указания модуля (неймспейса)
3 окт 18, 20:16    [21694550]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
Valery_B
Member

Откуда: Москва
Сообщений: 1827
aXS_new,

Создай
type
 TMyClass = class(TObject)
 end;

 TCustomClass = class of TObject;
...
RegisteredClasses:=TDictionary<string,TCustomClass>.Create(TIStringComparer.Ordinal);
RegisteredClasses.Add('TMyClass',TMyClass);
...
MyInstance:=RegisteredClasses['TMyClass'].Create;
3 окт 18, 21:30    [21694604]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
aXS_new
Guest
Всем спасибо за помощь, понемногу разбираюсь.

X-Cite, есть одна проблема. Пишу так:

TMyClass = class
	str: string;
end;

...

var
	obj: TList<TMyClass>;

...

TRttiContext.create().findType('TList<Unit1.TMyClass>').getMethod('create').invoke(obj, []);


И при запуске программы появляется ошибка памяти "Access violation". Может быть, что-то не так делаю?
3 окт 18, 22:26    [21694642]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1203
aXS_new
Всем спасибо за помощь, понемногу разбираюсь.

X-Cite, есть одна проблема. Пишу так:

TMyClass = class
	str: string;
end;

...

var
	obj: TList<TMyClass>;

...

TRttiContext.create().findType('TList<Unit1.TMyClass>').getMethod('create').invoke(obj, []);


И при запуске программы появляется ошибка памяти "Access violation". Может быть, что-то не так делаю?


Если метод Create - это конструктор, то в Invoke надо передавать не экземпляр объекта, а класс.
т.к. у вас десериализация, то передавать явно TList<TMyClass> нельзя, надо через метаданные Rtti как ниже...

var
  Rt: TRttiType;
...
Rt := TRttiContext.create().findType('TList<Unit1.TMyClass>');
obj := Rt.getMethod('create').invoke(Rt.AsInstance.MetaclassType, []).AsObject;


Естественно надо везде делать 100500 проверок, есть ли метод, конструктор ли он, вернулось ли значение, найден ли тип и т.д.
3 окт 18, 23:44    [21694693]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
andreymx
Member

Откуда: Запорожье
Сообщений: 49160
aXS_new
Всем спасибо за помощь, понемногу разбираюсь.

X-Cite, есть одна проблема. Пишу так:

TMyClass = class
	str: string;
end;

...

var
	obj: TList<TMyClass>;

...

TRttiContext.create().findType('TList<Unit1.TMyClass>').getMethod('create').invoke(obj, []);



И при запуске программы появляется ошибка памяти "Access violation". Может быть, что-то не так делаю?
радуйся, тебе повезло
что свалилось сегодня у тебя, а не завтра у пользователя
3 окт 18, 23:52    [21694698]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
aXS_new
Guest
X-Cite, спасибо. Но проблема еще и в том, что "asObject" возвращает объект класса "TObject", и компилятор выдает ошибку: "Incompatible types".
4 окт 18, 09:48    [21694828]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
DimaBr
Member

Откуда:
Сообщений: 10781
aXS_new
Можно ли создать объект по типу, записанному в строке?

Можно, собственно так всё и работает. По тексту их DFM создаётя форма и все компоненты на ней.
Курить TReader.ReadComponent до просветления и забыть про бесовщину - TRttiContext
4 окт 18, 09:55    [21694832]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
aXS_new
Guest
DimaBr, буду благодарен за разъяснения по поводу греховности использования TRttiContext.
4 окт 18, 09:58    [21694836]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
DimaBr
Member

Откуда:
Сообщений: 10781
Контекстов до Delphi 2010 этого не существовало.
4 окт 18, 10:39    [21694883]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
Dimonka
Member

Откуда:
Сообщений: 1125
DimaBr
aXS_new
Можно ли создать объект по типу, записанному в строке?

Можно, собственно так всё и работает. По тексту их DFM создаётя форма и все компоненты на ней.
Курить TReader.ReadComponent до просветления и забыть про бесовщину - TRttiContext

Не надо боятся RTTI.
4 окт 18, 10:45    [21694889]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1203
aXS_new
X-Cite, спасибо. Но проблема еще и в том, что "asObject" возвращает объект класса "TObject", и компилятор выдает ошибку: "Incompatible types".

Если у вас переменная obj конкретного типа, то тогда вместо AsObject, AsType<TList<TMyClass>>()
4 окт 18, 11:48    [21694993]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
aXS_new
Guest
X-Cite, но тип-то записан в строке, таким образом сделать не получится.
4 окт 18, 12:09    [21695027]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1203
aXS_new
X-Cite, но тип-то записан в строке, таким образом сделать не получится.

Так он же переменную объявил как-то...
Если он ее объявил как
var
obj: TList<TMyClass>
значит в этом контексте кода он знает про него.
4 окт 18, 12:34    [21695068]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
aXS_new
Guest
X-Cite, в том и дело, что переменная в моем случае - свойство класса, к которому обращаюсь через rtti, и таких свойств может быть много, а имена типов считываются в строку. То есть, написать "TList<TMyClass>" не могу, так как на этом месте может быть стоять и любой другой тип: "TMyClass", "TMyClass2", "TList<TMyClass2>" и т.д.
4 окт 18, 13:14    [21695126]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1203
aXS_new
X-Cite, в том и дело, что переменная в моем случае - свойство класса, к которому обращаюсь через rtti, и таких свойств может быть много, а имена типов считываются в строку. То есть, написать "TList<TMyClass>" не могу, так как на этом месте может быть стоять и любой другой тип: "TMyClass", "TMyClass2", "TList<TMyClass2>" и т.д.

Так а в чем проблема через Rtti в свойство записать значение?
MainObj := TRttiContext.Create().FindType('TMain').GetMethod('Create').Invoke(obj, []).AsObject;
Value := TRttiContext.Create().FindType('TMyClass').GetMethod('Create').Invoke(obj, []);
TRttiContext.Create.FindType('TMain').GetProperty('Obj').SetValue(MainObj, Value);
4 окт 18, 14:13    [21695223]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
aXS_new
Guest
X-Cite, проблема в том, что типы данных могут быть разными, в том числе, и "составными". Вот, как пример:

TMyClass = class
	private
		FA: TClassA;
		FB: TClassB;
		FC: TList<TClassC>;
	published
		property A: TClassA read...
		...
end;


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

А со свойством "FC: TList<TClassC>" как раз, трудности. Сначала нужно создать сам объект: TList<TClassC>, затем - в цикле записать в него элементы: FC[i]: TClassC.

Вы привели много хорошего кода, но как сделать это, я так и не понял. В итоге, в любом случае, наталкиваемся на то, что имя класса доступно только в строковом виде, а ситуация требует указателя на тип. Если подскажете, как это реализовать применительно к моему примеру, буду очень благодарен.
4 окт 18, 18:20    [21695631]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
DimaBr
Member

Откуда:
Сообщений: 10781
ЖЕСТЬ !!!
4 окт 18, 18:51    [21695675]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 10642
aXS_new
а имена типов считываются в строку
А зачем вы так делаете? Работайте с TRttiType
4 окт 18, 18:57    [21695688]     Ответить | Цитировать Сообщить модератору
 Re: Cоздать объект по типу, записанному в строке  [new]
X-Cite
Member

Откуда: Минск
Сообщений: 1203
вот за 10 минут на коленке набросал... конечно надо делать все красиво с кешированием типов, объектов, узлов xml и т.п. чтобы не вычислять их каждый раз... Но для понимания сути достаточно...

+
  TClassA = class
  private
    FInt: Int32;
    FLong: Int64;
  end;

  TClassB = class
  private
    FStr: string;
  end;

  TMyClass = class
  private
    FA: TClassA;
    FB: TClassB;
  end;


+
type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    function ConvertVariantToValue(const aValue: Variant; aRttiType: TRttiType): TValue;
    procedure FillField(aObject: TObject; aTypeInfo: PTypeInfo; aNode: IXMLNode);
    function CreateClass(aNode: IXMLNode; aTypeInfo: PTypeInfo): TValue;
  public
    function Deserialize<T>(aNode: IXMLNode): T;
  end;

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
var
  Obj: TMyClass;
begin
  Obj := Deserialize<TMyClass>(LoadXMLData('<TMyClass><FA><FInt>5</FInt><FLong>8</FLong></FA><FB><FStr>asd</FStr></FB></TMyClass>').DocumentElement);
  // В Obj будет экземпляр нашего класса.
end;

function TForm1.ConvertVariantToValue(const aValue: Variant; aRttiType: TRttiType): TValue;
var
  Temp: Variant;
  Value: TValue;
begin
  TValue.Make(nil, aRttiType.Handle, Value);
  Temp := Value.AsVariant();
  Temp := VarAsType(aValue, TVarData(Temp).VType);
  Exit(TValue.FromVariant(Temp));
end;

function TForm1.CreateClass(aNode: IXMLNode; aTypeInfo: PTypeInfo): TValue;
begin
  Exit(TRttiContext.Create().GetType(aTypeInfo).AsInstance.MetaclassType.Create());
end;

function TForm1.Deserialize<T>(aNode: IXMLNode): T;
var
  Value: TValue;
begin
  Value := CreateClass(aNode, TypeInfo(T));
  FillField(Value.AsObject(), TypeInfo(T), aNode);
  Exit(Value.AsType<T>());
end;

procedure TForm1.FillField(aObject: TObject; aTypeInfo: PTypeInfo; aNode: IXMLNode);
var
  Rt: TRttiInstanceType;
  Rf: TRttiField;
  Value: TValue;
  Value2: TValue;
begin
  Rt := TRttiContext.Create().GetType(aTypeInfo).AsInstance;
  for Rf in Rt.GetDeclaredFields() do
  begin
    case Rf.FieldType.TypeKind of
      TTypeKind.tkInteger,
      TTypeKind.tkInt64,
      TTypeKind.tkUString:
        begin
          Value := ConvertVariantToValue(aNode.ChildNodes.FindNode(Rf.Name).NodeValue, Rf.FieldType);
        end;
      TTypeKind.tkClass:
        begin
          Value := CreateClass(aNode.ChildNodes.FindNode(Rf.Name), Rf.FieldType.Handle);
          FillField(Value.AsObject(), Rf.FieldType.Handle, aNode.ChildNodes.FindNode(Rf.Name));
        end;
    end;
    Rf.SetValue(aObject, Value);
  end;
end;
4 окт 18, 19:14    [21695710]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / Delphi Ответить