Форум: "Основная";
Текущий архив: 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.57 MB
Время: 0.042 c