Як викликати private метод класу

Андрій Руфін, Майстра Delphi

Іноді виникає необхідність викликати private метод іншого класу, розташованого в іншому модулі. Це суперечить принципам ООП, закладеним в Delphi, але все-таки спробуємо це зробити. Для прикладу розглянемо випадок, коли потрібно зберегти / прочитати всі властивості об'єкта спадкоємця TPersistent, Наприклад об'єкта класу TFont.

У Delphi є стандартні класи TReader,TWriter розроблені для збереження / читання властивостей об'єкта. У цих класах нам цікаві методи TWriter.WriteProperties(Instance: TPersistent) І TReader.ReadProperty(AInstance: TPersistent). Метод WriteProperties дозволяє зберегти в потік всі властивості об'єкта спадкоємця TPersistent. Виклик у циклі методу ReadProperty дозволяє прочитати з потоку всі збережені раніше властивості.


Розглянемо збереження властивостей.

У Delphi5 все просто. Оголошення методу WriteProperties знаходиться в Protected секції класу TWriter. Викликати його особливих проблем не складе:

type
  THackWriter = class(TWriter);
  ....
  THackWriter(Writer).WriteProperties(Instance); / / Виклик методу
  .... 

У Delphi4 все трохи складніше. Метод WriteProperties знаходиться в private секції класу TWriter. Стандартно викликати цей метод можна тільки в рамках модуля де знаходиться клас TWriter тобто модуля classes.pas. Все, скажете ви, ситуація безнадійна, адже додати свій код в стандартний модуль classes.pas не можна, викликати метод WriteProperties з іншого модуля теж не можна. Але я хочу показати, що вихід з цієї ситуації є. Для початку зауважимо що:

Для виклику методу WriteProperties нам необхідно дізнатися його адресу. Спробуємо його дізнатися через public метод WriteCollection. Зробимо простенький проект, в якому буде виклик методу WriteCollection. Поставимо точку зупину на виклик методу WriteCollection. Запустимо проект і дійдемо до точки зупину. Відкриємо CPU window і увійдемо в метод WriteCollection, Натискаючи F7 (Trace Info). А тепер найцікавіше: у методі WriteCollection знайдемо виклик методу WriteProperties і обчислимо зміщення (в байтах) команди call TWriter.WriteProperties щодо початку методу WriteCollection. У нашому випадку воно дорівнює $ 36 +1 байт. І так, код для визначення адреси методу WriteProperties буде виглядати так:

var
  p: pointer;
  ....  
  p := @TWriter.WriteCollection;
  Inc(PByte(p), $37);
  Inc(PByte(p), PInteger(p)^+4);

додамо кілька додаткових перевірок для підвищення надійності цієї коду

var
  p: pointer;
  ....  
  p := @TWriter.WriteCollection;
  Inc(PByte(p), $37);
  if PByte(PChar(p)-1)^<>$E8 then begin exit; end;
  Inc(PByte(p), PInteger(p)^+4);
  if PByte(p)^<>$55 then begin exit; end;

адреса методу WriteProperties у нас вже є, залишилося тільки його викликати

  asm
    push eax
    push edx
    mov eax, Writer
    mov edx, Instance
    call p
    pop edx
    pop eax
  end;

Аналогічно можна обчислити адресу TReader.ReadProperty.
Для Delphi3, CBuilder3, 4 доведеться провести всі перераховані вище операції ще раз.

У результаті ми отримали код, який можна використовувати для збереження в потік / читання з потоку всіх властивості будь-якого об'єкта спадкоємця TPersistent.

Де його можна використовувати?
Наприклад, можна запам'ятати TEdit.Font або TForm.Icon або TImage.Picture.

У чому переваги цього методу?
Ми створили універсальні методи для збереження / читання всіх властивостей будь-якого об'єкта спадкоємця TPersistent, Отримали невеликий за розміром код. І в кінцевому підсумку навчилися викликати private методи іншого класу.

У чому його недоліки?
"Поганий стиль" програмування, в обхід принципів ООП. Тепер наш код неявним чином залежить від модуля classes.pas. Будь-яка зміна в модулі classes.pas, В об'єктах TWriter, TReader, в методах TWriter.WriteCollection, TReader.ReadCollection може узгодить до збоїв у роботі розробленого нами коду. Причому ми не зможемо побачити це на етапі компіляції програми, тільки в момент його роботи. Але чи часто ви зраджували і перекомпілювати модуль classes.pas? мені здається, що не дуже.

Весь код, наведений вище, ви можете отримати, завантаживши демонстраційний проект. Ви можете використовувати його на свій страх і ризик у своїх додатках. Але основне що я хотів вам показати – використовуючи нестандартні методи програмування можна домогтися цікавих результатів, навіть викликати private метод класу знаходиться в іншому модулі.

Все описане вище виникло при роботі над моїм shareware проектом – Storage library. Там цей прийом використовується для збереження / читання всіх властивостей об'єктів спадкоємців TPersistent наприклад TEdit.Font або TForm.Icon або TImage.Picture. Ви можете детальніше ознайомитися зі Storage library на www сервері компанії DeepSoftware

Download Розмір Опис
CallPrivate.zip 4.5kb Демонстраційний проект. Delphi3/4/5 CBuilder 3/4/5 версія.

  
Увага! Передрук даної
статті або її частини без узгодження з автором. Якщо ви хочете мати цю
статтю на своєму сайті або видати в друкованому вигляді, зв'яжіться з автором.
Автор статті:Андрій Руфін
  

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*