Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2005.07.18;
Скачать: [xml.tar.bz2];

Вниз

Список полей класса. Можно как то получить в 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;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.58 MB
Время: 0.032 c
1-1120107589
Тучудище
2005-06-30 08:59
2005.07.18
Подскажите плиз каким образом можно вернуть значение в переменную


14-1119344907
Иксик
2005-06-21 13:08
2005.07.18
Оранжевые в Баку


14-1119416218
Ega23
2005-06-22 08:56
2005.07.18
С днём рождения! 22 июня


14-1119623829
WondeRu
2005-06-24 18:37
2005.07.18
Ошибка "Класс TQuery не найден"


6-1113123235
Петя Иванов
2005-04-10 12:53
2005.07.18
загрузка страницы в браузере





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский