Компонент Delphi Читать и писать механизм (i)
1. Введение в объекты потоковых (потоки) и объекты чтения-записи (файлы)
В объектно-ориентированном программировании объектное управление данными занимает очень важную позицию. В Delphi метод поддержки объектного управления данными является одной из основных функций.
Delphi-это интегрированная среда разработки, которая объединяет объектно-ориентированный визуальный дизайн с объектно-ориентированными языками. Ядро Дельфи - это компоненты. Компоненты являются типом объекта. Приложения Delphi полностью построены с помощью компонентов, поэтому разработка высокопроизводительных приложений Delphi неизбежно будет включать технологию управления объектами на основе объектов.
Управление данными на основе объекта включает в себя два аспекта:
● Используйте объекты для управления данными
● Управление различными объектами данных (включая объекты и компоненты)
Delphi атрибуты объектов на основе объектных классов управления данными для потоковой передачи объектов (потока) и объектов FILER (Filers) и применяет их ко всем аспектам библиотеки класса визуальных компонентов (VCL). Они предоставляют богатые функции для управления объектами в памяти, внешней памяти и ресурсах Windows.
Объект потока, также известный как потоковой объект, является общим термином для Tstream, ThandLestream, TfileStream, Tmemorystream, Tresourcestream и Tblobstream. Они представляют возможность хранить данные в различных носителях, абстрагируя операции управления различными типами данных (включая объекты и компоненты) в полях памяти, вне памяти и базы данных в методах объектов, и в полной мере используют объектно-ориентированные технологические преимущества Из этого приложения могут довольно легко копировать данные в различных объектах потока.
Читать и записать объекты (файлы) включают объекты Tfiler, объекты прометки и объекты Twriter. Объект Tfiler является основным объектом для чтения и написания файлов, а также основным использованием прометанцев и Twriter в приложениях. Объекты как промежуточного, так и Twriter наследуются непосредственно от объектов Tfiler. Объект Tfiler определяет основные свойства и методы объекта Filer.
Объекты файла в основном выполняют две основные функции:
● Доступ Формы и компоненты в файлах формы
● Предоставьте буферизацию данных для ускорения считывания данных и операций с написанием данных
Чтобы иметь перцептивное понимание потоковых объектов и читать и записать объекты, давайте сначала рассмотрим пример.
а) Напишите файл
Процедура tfomr1.writedata (отправитель: tobject);
Вар
FileStream: tfileStream;
Mywriter: Twriter;
я: целое число
Начинать
FileStream: = tfilestream.create ('c: /test.txt', fmopenwrite); // Создать объект потока файла
MyWriter: = Twriter.Create (FileStream, 1024);
Mywriter.writelistbegin;
Для i: = 0 в memo1.lines.count-1 do
Mywriter.writestring (memo1.lines [I]);
Mywriter.writelistend;
FileStream.seek (0, sofrombeginning);
Mywriter.free;
FileStream.free;
Конец;
б) Прочтите файл
Процедура tform1.readdata (отправитель: tobject);
Вар
FileStream: tfileStream;
MyReader: Treader;
Начинать
FileStream: = tfilestream.create ('c: /test.txt', fmopenread);
MyReader: = trreader.create (FileStream, 1024);
MyReader.readListBegin;
Memo1.lines.clear;
В то время как не myreader.endoflist do // Обратите внимание на метод прометанца: endoflist
Начинать
Memo1.lines.add (myreader.readstring);
Конец;
Myreader.readlistend;
Myreader.free;
FileStream.free;
Конец;
Вышеупомянутые два процесса представляют собой один для процесса написания, а другой для процесса чтения. Процесс написания использует Twriter для сохранения контента (текстовой информации) в записке в качестве двоичного файла, сохраненного на диске с использованием tfileStream. Процесс чтения является как противоположность процессу написания. Запуск программы может видеть, что процесс чтения верно восстанавливает информацию, сохраненную в процессе написания.
На следующем рисунке описывается взаимосвязь между объектами данных (включая объекты и компоненты), потоковыми объектами, а также считывает и записывает объекты.
Рисунок (1)
Стоит отметить, что чтение и записи объектов, таких как объекты Tfiler, объекты прометанцев и объекты Twriter, редко называют авторами приложений непосредственно. и написать компонентный механизм.
Для потокового потока объектов много справочных материалов введено подробно, в то время как справочные материалы для объектов Tfiler, объектов прометки и объектов Twriter, особенно механизмов чтения и записи компонентов, являются редкими. Полем
2. Чтение и написание объектов (FILER) и механизма чтения компонентов
Объект Fileer в основном используется для доступа к файлам форм Delphi и компонентам в файлах формы, поэтому для четкого понимания объекта файла вы должны четко провести структуру файлов формы Delphi (файлы DFM).
Файлы DFM используются для форм хранения Delphi. Формы являются ядром визуального программирования Delphi. Форма соответствует окну в приложении Delphi, визуальные компоненты в форме соответствуют элементам интерфейса в окне, а также не визуальные компоненты, такие как Ttimer и Tavendialog, соответствующие определенной функции приложения Delphi. Дизайн приложения Delphi фактически сосредоточен на проектировании формы. Поэтому файлы DFM также занимают очень важную позицию в дизайне приложений Delphi. Все элементы в форме, включая собственные свойства формы, включены в файл DFM.
В окне приложения Delphi элементы интерфейса взаимосвязаны отношениями владения, поэтому структура дерева является наиболее естественным выражением; Файлы DFM физически хранятся в тексте (ранее хранящиеся как двоичные файлы в Delphi2.0), и логически они распоряжаются отношениями каждого компонента в структуре дерева. Из этого текста вы можете увидеть структуру дерева формы. Вот содержимое файла DFM:
Объект Форма1: tform1
Слева = 197
Верх = 124
...
Pixelsperinch = 96
Textheight = 13
Объект Кнопка1: Tbutton
Слева = 272
...
Подпись = 'Button1'
Taborder = 0
конец
Панель объекта1: Tpanel
Слева = 120
...
Подпись = 'Панель1'
Taborder = 1
Флакторе объекта1: Tcheckbox
Слева = 104
...
Подпись = 'Checkbox1'
Taborder = 0
конец
конец
конец
Этот файл DFM генерируется TWRITER через потоковую передачу объектов. Полем
Когда программа начинает работать, Treader считывает форму и компоненты через потоку объекта потока, потому что, когда Delphi компилирует программу, он использует инструкцию по компиляции {$ r *.dfm} для составления информации файла DFM в исполняемый файл с помощью компиляции Инструкция {$ r *.dfm}.
Требук и Twriter могут не только читать и писать большинство стандартных типов данных в объекте Pascal, но и читать и писать расширенные типы, такие как список и вариант, а также даже чтение и писать ощущения и компоненты. Тем не менее, сами протерок и Twriter на самом деле обеспечивают очень ограниченные функции, и большая часть реальной работы выполняется Tstream, очень мощным классом. Другими словами, прометанчик и Twriter на самом деле являются просто инструментами, которые отвечают только за то, как читать и писать компоненты.
Поскольку Tfiler является публичным предком -классом прометка и Twriter, чтобы понять прометанку и Twriter, начните с Tfiler.
Tfiler
Давайте сначала посмотрим на определение класса Tfiler:
Tfiler = class (tobject)
Частный
Fstream: tstream;
Fbuffer: указатель;
Fbufsize: целое число;
Fbufpos: целое число;
Fbufend: целое число;
FROUT: TCOMPONENT;
Flookuprot: TComponent;
Fancestor: Tpersistent;
Fignorechildren: логический;
защищен
процедура SetRoot (значение: TCOMPONT);
публичный
Конструктор Create (Stream: TSTREAM; BUFSIZE: Integer);
разрушитель разрушает;
Процедура DefineProperty (const name: String;
ReadData: TreaderProc;
Hasdata: логический);
Процедура DefineBinaryProperty (const name: String;
Readdata, writedata: tstreamproc;
Hasdata: логический);
Процедура Flushbuffer;
Свойство root: tcomponent Read froot write setroot;
Собственность Lookuproot: Tcomponent Read Flookuproot;
Предок имущества: Tpersistent Read Fancestor написать Fancestor;
Собственность Игнорехилды: логическое чтение Fignorechildrens написать Fignorechildren;
конец;
Объект Tfiler - это абстрактный класс прометки и Twriter, который определяет основные свойства и методы, используемые для хранения компонентов. Определяет корень. Сделано объектом потока. Следовательно, до тех пор, пока среда, которая доступна для объекта потока, компонент может быть доступен объектом файла.
Объект Tfiler также предоставляет два публичных метода, которые определяют свойства: DefineProperty и DefineBinaryProperty, которые позволяют объекту читать и записывать свойства, которые не определены в опубликованной части компонента. Давайте сосредоточимся на этих двух методах ниже.
Метод DefineProperty () используется для сохранения стандартных типов данных, таких как строки, целые числа, логические, символы, плавающие точки и перечисления.
В методе DefineProperty. Параметр имени указывает имя атрибута, который должен быть записан в файл DFM, который не определен в опубликованной части класса.
Параметры ReadData и writataTA указывают метод для чтения и записи необходимых данных при доступе к объекту. Типы параметров readdata и параметров wriTata являются PreaderProc и TwriterProc соответственно. Эти два типа объявлены так:
Prederproc = процедура (читатель: прометанчик) объекта;
Twriterproc = процедура (писатель: дважды) объекта;
Параметр Hasdata определяет, есть ли свойство, которые хранятся в среде выполнения.
Метод DefineBinaryProperty имеет много сходства с DefineProperty.
Давайте объясним использование этих двух методов ниже.
Мы поместили невизуальный компонент, такой как Ttimer в форму. ?
Откройте файл DFM этой формы, и вы можете увидеть несколько строк, похожих на следующее:
Timer1 Object1: Ttimer
Слева = 184
Верх = 149
конец
Потоковая система Delphi может сохранить только опубликованные данные, но Ttimer не опубликовал левые и верхние атрибуты, так как же эти данные сохраняются?
Ttimer - это производный класс TComponent.
Процедура tcomponent.defineProperties (FILER: TFILER);
вар
Предок: TComponent;
Информация: Longint;
Начинать
Информация: = 0;
Предок: = tcomponent (fileer.ancestor);
Если предок <> nil, то info: = ancestor.fdesigninfo;
Fileer.defineproperty («слева», Readleft, Writeleft,
Longrec (fdesigninfo) .lo <> longrec (info) .lo);
FILER.DefineProperty («Top», ReadTop, WriteTop,
Longrec (fdesigninfo) .hi <> longrec (info) .hi);
конец;
DefineProperties Tcomponent - это виртуальный метод, который перезаписывает свой предок класс Tpersistent, а в классе Tpersistent Этот метод является пустым виртуальным методом.
В методе DefineProperties мы видим, что в качестве параметра существует объект файла. ценить. Он называет метод DefineProperty Tfiler и определяет Readleft, Writeleft, Readtop, методы WriteTop для чтения и написания левых и верхних свойств.
Следовательно, любой компонент, полученный из TComponent, даже если у него нет оставшихся и верхних атрибутов, будет иметь два таких свойства при потоковой передаче в файл DFM.
В процессе поиска данных было обнаружено, что лишь немногие данные включают в себя компонентные механизмы чтения и написания. Поскольку процесс написания компонентов завершен Delphi IDE на этапе проектирования, его нельзя отслеживать для его процесса работы. Поэтому автор понимает механизм чтения компонента, отслеживая исходный код VCL во время работы программы, и анализирует механизм написания компонента с помощью механизма чтения и Twriter. Таким образом, следующее объясняет механизм чтения и написания компонентов в соответствии с этим процессом мышления, сначала рассказывая о промежуточном, а затем Twriter.
Прометанчик
Сначала посмотрите на файлы проекта Delphi, и вы найдете несколько строк кода, как это:
Начинать
Application.initialize;
Application.createform (tform1, form1);
Application.Run;
конец.
Это вход в программу Delphi. Проще говоря, значение этих строк кода: Приложение. Инициализация выполняет некоторую необходимую работу по началу запуска приложений. Полем
Что нас больше всего волнует, так это предложение создания формы. Как формируются формы и компоненты на форме? Как упоминалось ранее: все компоненты в форме, включая свойства самой формы, включены в файл DFM. в исполняемый файл. Поэтому можно сделать вывод, что при создании формы вам нужно читать информацию о DFM.
Следуя программе шаг за шагом, вы можете обнаружить, что программа вызывает метод прометки RideRootComponent во время создания формы. Цель этого метода состоит в том, чтобы прочитать корневой компонент и все компоненты, которые у него есть. Давайте посмотрим на реализацию этого метода:
функция Treader.readRootComponent (root: tcomponent): tcomponent;
...
Начинать
ReadSignature;
Результат: = NIL;
GlobalNamespace.beginWrite;
пытаться
пытаться
Readprefix (flags, i);
Если root = nil, тогда
Начинать
Результат: = tcomponentClass (findClass (readstr)). Create (nil);
Result.name: = readstr;
конец еще
Начинать
Результаты: = root;
Readstr;
Если csdesigning in result.componentState, тогда
Readstr else
Начинать
Include (result.fcomponentstate, csloading);
Include (result.fcomponentstate, csreading);
Result.name: = findUniquename (readstr);
конец;
конец;
FROUT: = Результаты;
Ffinder: = tclassfinder.create (tpersistentClass (result.classtype), true);
пытаться
Flookuprot: = результаты;
G: = Globalloaded;
Если g <> nil, тогда
Floaded: = g else
Floaded: = tlist.create;
пытаться
Если floaded.indexof (frout) <0 тогда
Floaded.add (froot);
FOWNER: = FROUT;
Включить (froot.fcomponentstate, csloading);
Включить (froot.fcomponentState, csreading);
Froot.readstate (self);
Exklide (froot.fcomponentstate, csreading);
Если g = nil, тогда
для i: = 0 до floaded.count - 1 do tomponent (floaded [i]). Загружен;
Окончательно
Если g = nil, то floaded.free;
Floaded: = nil;
конец;
Окончательно
Ffinder.free;
конец;
...
Окончательно
GlobalNamespace.endWrite;
конец;
конец;
READROOTCONENT First Calls ReadSignature для чтения тега объекта FILER ('TPF0'). Обнаружение тегов перед загрузкой объектов может предотвратить небрежность и неэффективное или устаревшее показания данных.
Давайте посмотрим на readprefix (флаги, i). Когда объект записи записывает компонент в потоку, он предварительно питает два значения перед компонентом Второе значение указывает на порядок, который он был создан в форме предка.
Затем, если корневой параметр равен нулю, новый компонент создается с именем класса, считанным Readstr, а свойство компонента считывается из потока; Полем
Froot.readstate (self);
Это очень критическое предложение. Хотя этот метод Readstate является методом TCOMPONT, можно найти дальнейшее отслеживание, которое на самом деле, наконец, обнаружил метод прометки RideDatainner.
Процедура Treader.readDatainner (экземпляр: tcomponent);
вар
OldParent, Oldouder: Tcomponent;
Начинать
в то время как не endoflist do ReadProperty (экземпляр);
Readlistend;
OldParent: = родитель;
Олдебл: = Владелец;
Родитель: = exant.getChildParent;
пытаться
Владелец: = exants.getChildowner;
Если не назначено (владелец), то владелец: = root;
в то время как не endoflist do ReadComponent (NIL);
Readlistend;
Окончательно
Родитель: = OldParent;
Владелец: = Олдеб;
конец;
конец;
Есть эта строка кода:
в то время как не endoflist do ReadProperty (экземпляр);
Это используется для чтения свойств корневого компонента. Для этих двух различных свойств должно быть два различных метода чтения.
Процедура Treader.readProperty (Ainstance: Tpersistent);
...
Начинать
...
Propinfo: = getPropinfo (ancess.classinfo, fpropName);
Если Propinfo <> nil, то readpropvalue (экземпляр, пропинфо) иначе
Начинать
{Не может надежно восстановиться после ошибки в определенном свойстве}
Fcanhandleexcepts: = false;
Ancess.defineproperties (self);
Fcanhandleexcepts: = true;
Если fpropName <> '', то тогда
PropertyError (fpropName);
конец;
...
конец;
Чтобы сохранить пространство, какой -то код был опущен.
Propinfo: = getPropinfo (ancess.classinfo, fpropName);
Этот код должен получить информацию опубликованного свойства fpropName. Из следующего кода мы видим, что если информация об атрибуте не является пустой, значение атрибута считывается с помощью метода readPropValue, а метод readPropValue считывает значение атрибута через функцию RTTI, которая не будет введено подробно здесь. Если информация об атрибуте пуста, это означает, что атрибут fpropName не опубликована, и его следует прочитать через другой механизм. Это метод DefineProperties, упомянутый выше, следующим образом:
Ancess.defineproperties (self);
Этот метод фактически вызывает метод DefineProperty промежуточного:
Процедура Treader.DefineProperty (Const name: String;
Readdata: Treaderproc;
Начинать
Если sametext (имя, fpropname) и назначено (readdata), тогда
Начинать
Readdata (self);
FpropName: = '';
конец;
конец;
Сначала сравнивается, является ли имя атрибута чтения таким же, как и имя атрибута.
ОК, корневой компонент был прочитал, и следующий шаг должен быть для чтения компонентов, принадлежащих корневому компоненту. Давайте снова посмотрим на метод:
Процедура Treader.readDatainner (экземпляр: tcomponent);
Существует код, следуя этому методу:
в то время как не endoflist do ReadComponent (NIL);
Это именно то, что используется для чтения дочерних компонентов. Механизм чтения дочернего компонента такой же, как и чтение корневого компонента, представленного выше, что является глубоким обходом дерева.
До сих пор был введен механизм считывания компонентов.
Давайте посмотрим на механизм написания компонентов. Когда мы добавляем компонент в форму, связанные с ним свойства будут сохранены в файле DFM, и этот процесс выполняется Twriter.
Ø Twriter
Объект Twriter - это мгновенный объект Filer, который записывает данные в поток. Объект Twriter унаследован непосредственно от Tfiler.
Объект Twriter предоставляет много методов для записи различных типов данных в потоку. Поэтому, чтобы освоить методы реализации и применения объектов Twriter, вы должны понимать формат объектов автора, хранящих данные.
Первое, что следует отметить, это то, что каждый поток объекта Filer содержит теги объекта Filer. Этот тег занимает четыре байта, а его значение - «TPF0». Объект Fileer обращается к тегу для методов wriseSignature и RequieNature. Этот тег в основном используется для руководства операциями чтения, когда объекты считывания читают данные (компоненты и т. Д.).
Во -вторых, объект писателя должен оставить бит -флаг -байт, прежде чем хранить данные, чтобы указать, какой тип данных хранится позже. Этот байт является значением типа TVALUETYPE. TVAlueType - это тип перечисления, который занимает одно байтовое пространство, и его определение следующим образом:
TVALUETYPE = (Vanull, Valist, Vaint8, Vaint16, Vaint32, Vaentended, Vastring, Vaident,
Vafalse, vatrue, vabinary, vaset, valstring, vanil, vougolection);
Следовательно, в реализации каждого метода написания данных объекта писателя вы должны сначала написать бит флага, а затем написать соответствующие данные; Чтение данных. Логотип Valist имеет особую цель. Поэтому при написании нескольких последовательных идентичных элементов в объекте писателя сначала используйте WriteListBegin, чтобы написать флаг Valist, и после написания элементов данных, затем напишите флаг Vanull; Используйте функцию Endoflist в середине, определите, есть ли флаг Vanull.
Давайте посмотрим на очень важный метод для Twriter writedata:
Процедура twriter.writedata (экземпляр: tcomponent);
...
Начинать
...
WritePrefix (Flags, fchildpos);
Если usequalifiednames, то
Writestr (gettypedata (ptypeinfo (ancess.classtype.classinfo)). UnitName + '.' + Ancement.classname)
еще
Writest (encember.classname);
Writest (exants.name);
Свойствопозиция: = позиция;
if (fancestorlist <> nil) и (fancestorpos <fancestorlist.count) тогда
Начинать
Если предок <> nil, то Inc (Fancestorpos);
Inc (fchildpos);
конец;
Writeproperties (экземпляр);
Писательская служба;
...
конец;
Из метода Writedata мы можем увидеть общую картину генерации информации о файле DFM. Сначала запишите флаг (префикс) перед компонентом, а затем напишите имя класса и имя экземпляра. Тогда есть такое предложение:
Writeproperties (экземпляр);
Это используется для написания свойств компонента. Как упоминалось ранее, в файлах DFM есть опубликованные атрибуты, так и не опубликованные атрибуты. Давайте посмотрим на реализацию WriteProperties:
Процедура Twriter.WriteProperties (экземпляр: Tpersistent);
...
Начинать
Count: = gettypedata (ancess.classinfo)^. Propcount;
Если счет> 0, тогда
Начинать
GetMem (proplist, count * sizeof (pointer));
пытаться
GetPropinfos (exants.classinfo, proplist);
для i: = 0, чтобы подсчитать - 1 сделать
Начинать
Propinfo: = proplist^[i];
Если propinfo = nil, тогда
Перерыв;
Если isstoredprop (экземпляр, пропинфо), тогда
WriteProperty (экземпляр, Propinfo);
конец;
Окончательно
Freemem (proplist, count * sizeof (pointer));
конец;
конец;
Ancess.defineproperties (self);
конец;
Пожалуйста, смотрите следующий код:
Если isstoredprop (экземпляр, пропинфо), тогда
WriteProperty (экземпляр, Propinfo);
Функция isstoredProp определяет, должна ли свойство сохранено, сохранив квалификацию.
После сохранения опубликованного имущества не публикуемое собственность должна быть сохранена.
Ancess.defineproperties (self);
Реализация DefineProperties была упомянута ранее.
Хорошо, пока все еще есть вопрос: как сохранены дочерние компоненты, принадлежащие корневым компоненту? Давайте посмотрим на метод WRITEDATA (этот метод был упомянут ранее):
Процедура twriter.writedata (экземпляр: tcomponent);
...
Начинать
...
Если не IgnoreChildren, тогда
пытаться
Если (Fancestor <> nil) и (Fancestor - это TComponent), тогда
Начинать
if (Fancestor - это Tcomponent) и (csinline в tcomponent (fancestor) .componentstate) тогда
Frootancestor: = tcomponent (Fancestor);
Fancestorlist: = tlist.create;
Tcomponent (Fancestor) .getChildren (Addancestor, Froutancestor);
конец;
Если csinline в exance.componentstate, то
FROUT: = экземпляр;
Exante.getChildren (WriteComponent, FROUT);
Окончательно
Fancestorlist.free;
конец;
конец;
Свойство IgnoreChildren позволяет писателю хранить компонент без хранения дочерних компонентов, принадлежащих компоненту. Если свойство IgnoreChildren верно, объект писателя не хранит дочерние компоненты, которые он имеет при хранении компонента. В противном случае подкомпоненты будут храниться.
Exante.getChildren (WriteComponent, FROUT);
Это наиболее важное предложение для написания подкомпонентов. Таким образом, то, что мы видим в файле DFM, представляет собой деревоподобную компонентную структуру.