Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.07.18;
Скачать: CL | DM;

Вниз

Список полей класса. Можно как то получить в runtime?   Найти похожие ветки 

 
Begin   (2005-06-27 17:14) [0]

GetPropList(self.ClassInfo... и т.д. не сработал. Вернее, сработал, но только для получения published-свойств... А можно ли как то получить список тех полей, которые не-published ???

Вот к примеру :

TSomeClass = class
 field1 : integer; //Вот эти поля сильно охота
 field2 : integer; //получить в runtime
 published
   property prop1; // это вот GetPropList"ом возвращется,
                   // только надо не его...
end;


Спасибо заранее...


 
begin...end ©   (2005-06-27 17:15) [1]

> Begin   (27.06.05 17:14)

> А можно ли как то получить список тех полей, которые
> не-published ???

Нет.


 
Begin   (2005-06-27 17:18) [2]

Лаконично. Категорично. Вызывает грусть. Выходит, при желании оперировать полями в рантайме, все оные должны стать именно published имеено property"s ????


 
evvcom ©   (2005-06-27 17:19) [3]

Насколько я помню, поля field1 и field2 как раз будут published.


 
Digitman ©   (2005-06-27 17:21) [4]

выходит - так


 
evvcom ©   (2005-06-27 17:21) [5]


> Выходит, при желании

А чем вызвано подобное желание?


 
begin...end ©   (2005-06-27 17:22) [6]

> evvcom ©   (27.06.05 17:19) [3]

Зависит от {M}.


 
Digitman ©   (2005-06-27 17:23) [7]


> Begin   (27.06.05 17:18) [2]


лезть в приватные данные "чужих" объектов как минимум не этично)


 
Begin   (2005-06-27 17:24) [8]

Ну дык... поля Field1 и Field2 будут видны при вызове любого экземпляра класса, однако если в классе отсутствует явная секция published, то вызов GetPropList вызывает виолейшн, а если секция явно прописана, то показывает только property"s из оной...


 
Digitman ©   (2005-06-27 17:27) [9]


> Begin   (27.06.05 17:24) [8]


а ты как хотел ?


 
Юрий Зотов ©   (2005-06-27 17:28) [10]

Уточнение.

Все эти списки основаны на RTTI, а она генерится только для свойств (а не для полей), только для published и только при включенной {$M+} (для самого класса или любого его предка).


 
Begin   (2005-06-27 17:30) [11]


> Digitman ©   (27.06.05 17:23) [7]
>
> > Begin   (27.06.05 17:18) [2]
>
>
> лезть в приватные данные "чужих" объектов как минимум не
> этично)

Ни БожеМой !!!! :) Это данные моего объекта, и они никак не приватные. Private-блока там вообче нет.


> evvcom ©   (27.06.05 17:21) [5]
>
> > Выходит, при желании
>
> А чем вызвано подобное желание?


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


 
Eraser ©   (2005-06-27 17:33) [12]

Begin   (27.06.05 17:30) [11]
Есть группа полей, которые есть потомки, но от других классов.


Попробуй что-то типа этого TObject(FMyObject).Create;


 
Digitman ©   (2005-06-27 17:37) [13]


> Private-блока там вообче нет


ну это, конечно же, твое право, хотя не воздержусь от предостережений на эту тему


> Есть группа полей, которые есть потомки, но от других классов


каждый класс из иерархии наследования, объявив некое поле, становится ответственен за его инициализацию

никаких "циклов" ! это - изначально бредовая идея ...


 
Eraser ©   (2005-06-27 17:44) [14]

Eraser ©   (27.06.05 17:33) [12]

Глупость написал...

Begin
Охота выполнить Create, и некий набор инициирующих действий не в тупую, а в цикле.


Прийдётся "в тупую" делать...


 
Юрий Зотов ©   (2005-06-27 17:47) [15]

> Begin   (27.06.05 17:30) [11]

> Есть группа полей, которые есть потомки, но от других
> классов. Охота выполнить Create, и некий набор инициирующих
> действий не в тупую, а в цикле. Опять, же подразумевается
> возможность увеличения или уменьшения списка полей в
> будущем....

type
 TMyClass = class(...)
 private
   FField0: TMyField0;
   ...
 public
   function GetFieldCount: integer; virtual;
   function GetField(Index: integer): TObject; virtual;
 ...
 end;

function TMyClass.GetFieldCount: integer;
begin
 Result := ...
end;

function TMyClass.GetField(Index: integer): TObject;
begin
 case Index of
   0: Result := FField0;
   ...
 end
end;

Если в потомке появились дополнительные поля, то он перекрывает эти 2 метода:

function TMyClassDescendant.GetFieldCount: integer;
begin
 Result := inherited GetFieldCount + ...
end;

function TMyClassDescendant.GetField(Index: integer): TObject;
var
 N: integer;
begin
 N := inherited GetFieldCount;
 if Index < N then
   Result : = inherited GetField(Index)
 else
   case Index - N of
     0: Result := ...;
     ...
   end
end;

Теперь, создав в программе такой объект, можем проходить в цикле по его полям и инициализировать их. Что и требовалось.

P.S.
Уменьшить список полей в потомке, как Вы понимаете, невозможно, его можно только увеличить. Для уменьшения же в любом случае придется править код - в том числе, и эти 2 метода.


 
Begin   (2005-06-27 17:49) [16]


> никаких "циклов" ! это - изначально бредовая идея ...


TSomeClass = class
 selfStatus : byte
 f1 : TOneClass;
 f2 : TTwoClass;
 ...
 fN : TNthClass;
end;

constructor TSomeClass.Create
 f1 := TOneClass.Create;
 if self.SelfStatus = 1 then TOneClass.init(1);
 if self.SelfStatus = 2 then TOneClass.init(2);

 // и т.д. ????????? Громоздко до жути получается
 // передать параметр сразу в Create по определенным причинам
 // не могу, отчего и существует метод init...
end;


 
Eraser ©   (2005-06-27 17:52) [17]

Begin   (27.06.05 17:49) [16]

А зачем init(1)?????


 
Digitman ©   (2005-06-27 17:52) [18]


> Begin   (27.06.05 17:49) [16]


какую роль играет поле SelfStatus ?
где и как оно иниц-ся ?


 
Begin   (2005-06-27 18:05) [19]


> Digitman ©   (27.06.05 17:52) [18]
>
> > Begin   (27.06.05 17:49) [16]
>
> какую роль играет поле SelfStatus ?
> где и как оно иниц-ся ?


Если 0 - готовить пустую форму для приема данных. Если 1 - получить данные из БД и разрешить их менять. Если 2 - получить из БД и показать в режиме только просмотра. Инициируется после создания базового класса в зависимости от установок программы.


 
Begin   (2005-06-27 18:08) [20]


> Юрий Зотов ©   (27.06.05 17:47) [15]

> Уменьшить список полей в потомке, как Вы понимаете, невозможно,
> его можно только увеличить. Для уменьшения же в любом случае
> придется править код - в том числе, и эти 2 метода.


Это да, понимаю. Просто объем изменений мог бы быть много меньше.


 
Eraser ©   (2005-06-27 18:09) [21]

Begin

1. Лучше использовать оператор case.
2. init перенести в конструктор создаваемого объекта.


 
begin...end ©   (2005-06-27 18:12) [22]

> Begin   (27.06.05 17:49) [16]

> TOneClass.init(1);

Init -- это классовый метод, что ли?


 
jack128 ©   (2005-06-27 18:19) [23]

Юрий Зотов ©   (27.06.05 17:28) [10]
а она генерится только для свойств (а не для полей),
 Э-э-э.. TObject.FieldAddress ??  
Begin   (27.06.05 17:14)
Поищи на delphikingdom статью про создание инспектора объектов. Там как раз реализовывалось что похожее на предложенное Юрой Зотовым..


 
Begin   (2005-06-27 18:19) [24]


> begin...end ©   (27.06.05 18:12) [22]
> > Begin   (27.06.05 17:49) [16]
> > TOneClass.init(1);
> Init -- это классовый метод, что ли?


Ээээ... В смысле ? Некоторые поля - экземпляры других классов, init - это их метод. Неклассовых методов не встречал... :))


> Eraser ©   (27.06.05 18:09) [21]
> Begin
>
> 1. Лучше использовать оператор case.
> 2. init перенести в конструктор создаваемого объекта.

Эт я для наглядности, только в форуме. Все равно ведь придется имена полей перечислить, и вызвать для каждого Create.


 
Begin   (2005-06-27 18:22) [25]


> jack128 ©   (27.06.05 18:19) [23]
> Юрий Зотов ©   (27.06.05 17:28) [10]
> а она генерится только для свойств (а не для полей),  Э-э-э..
> TObject.FieldAddress ??  


FieldAddress method (TObject)

Returns the address of a published object field.


 
begin...end ©   (2005-06-27 18:32) [26]

> Begin   (27.06.05 18:19) [24]

> В смысле ?

В смысле, есть методы класса, объявленные со словом class, например:

TOneClass = class
 class procedure Init(...);
end;


а есть обычные методы (объявленные без слова class)-- методы объекта.
Вот и непонятно, как можно обычный метод объекта вызвать в форме TOneClass.init(1), т.е.  Имя_Класса.Имя_Метода, а не в форме Имя_Экземпляра_Класса.Имя_Метода. Компилятор такое мог пропустить только для классового метода (или для конструктора, который тоже, в общем-то, по сути дела является классовым методом).


 
DiamondShark ©   (2005-06-27 18:40) [27]


> Юрий Зотов ©   (27.06.05 17:28) [10]
> Уточнение.
>
> Все эти списки основаны на RTTI, а она генерится только
> для свойств (а не для полей)

Не правда.
Она генерится для всего, что паблишед.


 
default ©   (2005-06-27 18:40) [28]

"Эт я для наглядности, только в форуме. Все равно ведь придется имена полей перечислить, и вызвать для каждого Create."
ёлки, ну так сделай массив объектов и в цикле обходи и вызывай свои конструкторы...


 
Суслик ©   (2005-06-27 18:42) [29]

для методов тоже генерится.


 
default ©   (2005-06-27 18:49) [30]

может тебе понравится создание объектов через ссылку на класс их общего класса при наличие виртуальных конструкторов
хотя всё равно придётся "забить" массив ссылок на класс создаваемых типов


 
Юрий Зотов ©   (2005-06-27 18:51) [31]

> jack128 ©   (27.06.05 18:19) [23]
> DiamondShark ©   (27.06.05 18:40) [27]

Не считаю правильным заниматься формальными придирками к словам, выдергивая их из контекста ветки. И ежику известно, что есть FieldAddress и пр. Но здесь-то ведь речь идет о получении списка полей?  Неужто не понятно, что это и имелось в виду.

Если не согласны - прошу привести код, дающий список published-полей. Как вы понимаете, список published-свойств получается легким движением руки - а вот как быть с полями?

Сорри, но лучше бы по делу что сказали...


 
Юрий Зотов ©   (2005-06-27 18:52) [32]

> Суслик ©   (27.06.05 18:42) [29]

Тоже см. [31].


 
Суслик ©   (2005-06-27 18:53) [33]

методы можно так перечислить
поля - не знаю.

//
// TMethodEnumerator
//

// from dUnit.pas from Delphi2005

type   TMethodEnumerator = class

     protected FMethodNameList: array of string;
     protected function GetNameOfMethod(Index: integer):  string;
     protected function GetMethodCount: Integer;

     public constructor Create(AClass: TClass);
     public property MethodCount: integer read GetMethodCount;
     public property NameOfMethod[index:  integer]: string read GetNameOfMethod;
  end;

constructor TMethodEnumerator.Create(AClass: TClass);
type
  TMethodTable = packed record
     count: SmallInt;
  end;
var
  table: ^TMethodTable;
  name:  ^ShortString;
  i, j:  Integer;
begin
  inherited Create;
  while aclass <> nil do
  begin
     // *** HACK ALERT *** !!!
     // Review System.MethodName to grok how this method works
     asm
        mov  EAX, [aclass]
        mov  EAX,[EAX].vmtMethodTable { fetch pointer to method table }
        mov  [table], EAX
     end;
     if table <> nil then
     begin
        name  := Pointer(PChar(table) + 8);
        for i := 1 to table.count do
        begin
           // check if we"ve seen the method name
           j := Low(FMethodNameList);
           while (j <= High(FMethodNameList)) and (name^ <> FMethodNameList[j]) do
              inc(j);

           // if we"ve seen the name, then the method has probably been overridden
           if j > High(FMethodNameList) then
           begin
              SetLength(FMethodNameList,length(FMethodNameList)+1);
              FMethodNameList[j] := name^;
           end;
           name := Pointer(PChar(name) + length(name^) + 7)
        end;
     end;
     aClass := aClass.ClassParent;
  end;
end;

function TMethodEnumerator.GetMethodCount: Integer;
begin
  Result := Length(FMethodNameList);
end;

function TMethodEnumerator.GetNameOfMethod(Index: integer): string;
begin
  Result := FMethodNameList[Index];
end;


 
Суслик ©   (2005-06-27 18:55) [34]

да, наверное я не о том :))
но все равно - может кому полезно будет.


 
jack128 ©   (2005-06-27 19:06) [35]

Юрий Зотов ©   (27.06.05 18:51) [31]
Есть сомнения, что поколдовав над исходниками FieldAddress мы получим таки список полей ??  К сожелению сейчас зашиваюсь, но через недельку думаю преставлю исходники.  Есть в System.pas такая структурка - PFieldTable, вот из неё информация и вытаскивается, только там формат не очевидный..


 
default ©   (2005-06-27 19:12) [36]

jack128 ©   (27.06.05 19:06) [35]
а нафиг?
всё равно для целей автора это ничё не даст поскольку поиск будет каждый раз вестись...
он хочет невозможного по ходу


 
DiamondShark ©   (2005-06-27 20:31) [37]


> Юрий Зотов ©   (27.06.05 18:51) [31]
> > jack128 ©   (27.06.05 18:19) [23]
> > DiamondShark ©   (27.06.05 18:40) [27]
>
> Не считаю правильным заниматься формальными придирками к
> словам, выдергивая их из контекста ветки.

Утверждение "Все эти списки основаны на RTTI, а она генерится только для свойств (а не для полей)" является чушью вне зависимости от контекста.


> Если не согласны - прошу привести код, дающий список published-полей.
> Как вы понимаете, список published-свойств получается легким
> движением руки - а вот как быть с полями?

А так же. Если функцию для полей не включили в TypInfo.pas, это ещё не значит, что информации для полей нету.

Для версии 5:

procedure TForm1.Button2Click(Sender: TObject);
type
 TFieldRec = packed record
   Offset: Integer;
   XXX: Word;
   Name: ShortString;
 end;
 PFieldRec = ^TFieldRec;
var
 tbl: Pointer;
 cls: TClass;
 Count: Word;
 i: Integer;
begin
 cls := Self.ClassType;
 repeat
   tbl := Pointer(Pointer(Integer(cls) + vmtFieldtable)^);
   if tbl <> nil then
     begin
       Count := Integer(tbl^);
       tbl := Pointer(Integer(tbl)+6);
       for i := 0 to Count - 1 do
         begin
           Memo1.Lines.Add(PFieldRec(tbl).Name);
           tbl := Pointer(Integer(tbl)+6+Length(PFieldRec(tbl).Name)+1);
         end;
     end;
   cls := cls.ClassParent;
 until cls = nil;
end;



> Сорри, но лучше бы по делу что сказали...

Ути-пути.


 
DiamondShark ©   (2005-06-27 20:33) [38]


> Count := Integer(tbl^);

Count := Word(tbl^);


 
Юрий Зотов ©   (2005-06-27 21:22) [39]

> jack128 ©   (27.06.05 19:06) [35]
> DiamondShark ©   (27.06.05 20:31) [37]

Вот так-то оно лучше. Очень рад, что мой "наезд" привел-таки к тому, что нужно было сказать и сделать СРАЗУ.

Гы...
:о)

> Begin

Cтруктуру записи о поле (это TFieldRec в [37]) я не знал, поэтому пришлось сделать небольшой экскурс в память компьютера и поисследовать таблицу полей (благо, что когда-то делал то же самое с таблице методов). Вот что в итоге получилось:

procedure GetPublishedFields(Obj: TObject; List: TStrings);

 procedure GetClassFields(AClass: TClass);
 var
   P: Pointer;

   procedure IncPtr(Offset: integer);
   begin
     P := Pointer(Integer(P) + Offset)
   end;

 var
   FieldCount: word;
   FieldOffset: integer;
   FieldName: ShortString;
   i: integer;
 begin
   if AClass <> nil then
   begin
     P := AClass;
     IncPtr(vmtFieldTable);
     P := Pointer(P^);
     if P <> nil then
     begin
       FieldCount := Word(P^);
       IncPtr(6);
       for i := 0 to FieldCount - 1 do
       begin
         FieldOffset := Integer(P^);
         IncPtr(6);
         FieldName := ShortString(P^);
         IncPtr(Byte(P^) + 1);
         List.AddObject(FieldName, TObject(FieldOffset))
       end
     end
   end
 end;

var
 AClass: TClass;
begin
 List.Clear;
 if Obj <> nil then
 begin
   AClass := Obj.ClassType;
   while AClass <> nil do
   begin
     GetClassFields(AClass);
     AClass := AClass.ClassParent
   end
 end
end;

Это работает, дает имена полей и их смещения (по которым легко получить адреса полей). Но не дает о полях никакой другой информации (впрочем, как и код [37]), поэтому, если тип поля заранее неизвестен, то работать с ним все равно практически нельзя, несмотря на известный адрес.

Поэтому лучше воспользоваться советом [35] и покопать в сторону PFieldTable. Судя по декларациям в System.pas, оттуда можно вытащить PTypeInfo каждого поля, а это уже дает возможность нормально работать с ним.


 
DiamondShark ©   (2005-06-27 21:48) [40]


> и покопать в сторону PFieldTable

А в Д5 такой нет. :(
Я из анализа FieldAddress вытаскивал.

Как она выглядит?



Страницы: 1 2 вся ветка

Текущий архив: 2005.07.18;
Скачать: CL | DM;

Наверх




Память: 0.59 MB
Время: 0.054 c
14-1119616196
TUser
2005-06-24 16:29
2005.07.18
Windows = DOS ???


4-1116854839
Dextor
2005-05-23 17:27
2005.07.18
Перехват всех сообщений программы


14-1119529290
X9
2005-06-23 16:21
2005.07.18
Схема советских колонок S90


1-1119943500
Asker
2005-06-28 11:25
2005.07.18
скачать файл из инета ...


4-1116935345
Ленин
2005-05-24 15:49
2005.07.18
Как определить обычный это компьютер или сервер?