Программирование - это просто
Advertisement
Главная arrow Уроки программирования arrow Delphi изнутри arrow TObject, демонстрация использования self (ClassType,TClass,self,TList,Pointer,Add, TClass)
29.05.2024 г.
Главное меню
Главная
Интернет магазин
Программные продукты
Биржевые роботы
Искусственный интеллект
Математика и информатика
1С:Предприятие
Уроки C#
Уроки Delphi
Уроки программирования
Web-программирование
Дизайн и графика
Компьютер для блондинок
Исходники
Статьи
Платный раздел
Рассказы про компьютеры
Хитрости и секреты
Системный подход
Размышления
Наука для чайников
Друзья сайта
Excel-это не сложно
Все о финансах
.
TObject, демонстрация использования self (ClassType,TClass,self,TList,Pointer,Add, TClass) Печать E-mail
Автор megabax   
15.10.2009 г.
New Page 2

TObject, демонстрация использования self (ClassType,TClass,self,TList,Pointer,Add, TClass)

Исходники к уроку можно скачать здесь.

Сегодня мы изучим функции ClassType. Реализован он очень просто:

function TObject.ClassType: TClass;
begin
      Pointer(Result) := PPointer(Self)^;
end;

Тип PPointer является указателем на указатель и объявлен следующим образом:

PPointer = ^Pointer;

 А TClass объявлен:

TClass = class of TObject;

Переменная Self является ссылкой на сам объект. Что бы было лучше понятно, что такое self и для чего он нужен, давайте рассмотрим пример. Предположим, у нас есть некий контейнер объектов, связанный с некоторым компонентом визуального отображения, в котором отображаются объекта нашего контейнера. При изменении любого из объектов в контейнере необходимо перерисовать визуальный объект. Но вот проблема - сам объект "не знает", что он принадлежит контейнеры. Как решить эту проблему? Логично предположить, в объекте нужно предусмотреть некоторое поле, указывающее на контейнер, которое будет заполнятся при добавлении объекта в контейнер. Можно каждый раз, когда добавляем, еще и "не забыть" присвоить указатель на контейнер. Но согласитесь, это не очень удобно. А вот как же сделать так, что бы эти действия производить в одном методе? Как раз с помощью указателя self. Давайте попробуем написать класс контейнер, содержащий слова. При измении любого слова в контейнере список слов должен обновиться. Для начала объявим  объявим класс слова и класс контейнера:

TWordSelfDemo=class(TObject)
private
      FOwner:pointer;
protected
      FWord:string;
      procedure SetWord(AWord:string);
public
      property Word:string read FWord write SetWord;
end;

TWordsContainerSelfDemo=class(TList)
private
     procedure Refresh;
protected
     FListBox:TListBox;
     procedure SetListBox(AListBox:TListBox);
public
     function Add(Item: Pointer): Integer;
     procedure delete(index:integer);
     property ListBox:TListBox read FListBox write SetListBox;
end;
 

Теперь немножко комментариев к коду. Класс TWordSelfDemo у нас будет хранить слово (его текст). Для задания слова используем свойство word, которое имеет метод для записи SetWord. При реализации этого метода мы предусмотрим обновление визуального компонента, связанного с контейнером.

Класс TWordsContainerSelfDemo - собственно говоря, сам контейнер. Это класс, дочерний от TList. В нем переопределены методы Add и Delete, а так же добавлен метод Refresh для обновления связанного с контейнером визуального компонента. Для связи с визуальным компонентом используется свойство ListBox, устанавливаемое методом SetListBox.

А теперь перейдем к реализации. Опишем метод SetWord

procedure TWordSelfDemo.SetWord(AWord:string);
begin
     if FWord=AWord then exit;
     FWord:=AWord;
     if FOwner<>nil then TWordsContainerSelfDemo(FOwner).Refresh;
end;

В данной процедуре происходит проверка, что мы меняем содержимое свойства Word - если пытаемся записать повторно то же самое слово - ничего не делаем.  Затем присваиваем новое значение внутреннему полю и запускаем метод Refresh класса контейнера. При чем стоит обратить внимание на то, что нужно еще и убедиться, что наше объект действительно находиться в контейнере - значение поля  FOwner проверяем на nil.

Все, у класса слова у нас только один метод. Мы его реализовали. Теперь займемся контейнером. Реализуем его метод Add:

function TWordsContainerSelfDemo.Add(Item: Pointer): Integer;
begin
    inherited Add(Item);
    TWordSelfDemo(Item).FOwner:=self;
    refresh;
end;

В данной функции мы вызываем метод Add родительского класса (TList), затем устанавливаем указатель на себя при помощи ключевого слова self:

    TWordSelfDemo(Item).FOwner:=self;

И, наконец, обновляем визуальный компонент - вызываем refresh.

Сам метод refresh будет у нас выглядеть так:

procedure TWordsContainerSelfDemo.Refresh;
var i,cn:integer;
begin
     FListBox.Clear;
     cn:=Count-1;
     for i:=0 to cn do FListBox.Items.Add(TWordSelfDemo(Items[i]).FWord);
end;

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

И, наконец, последние два метода, они комментариев, думаю, не требуют:

procedure TWordsContainerSelfDemo.SetListBox(AListBox:tListBox);
begin
     FListBox:=AListBox;
     Refresh;
end;

procedure TWordsContainerSelfDemo.delete(index:integer);
begin
    TWordSelfDemo(Items[index]).Free;
    inherited Delete(index);
    refresh;
end;

А теперь сделаем форму*:

TObject, демонстрация использования self (ClassType,TClass,self,TList,Pointer,Add, TClass)

Саму форму назовем SelfDemoForm, кнопочки btnAdd (добавить), btnDelete (удалить) и btnEdit (редактировать).  Сроку для ввода слова назовем edWord, а сипоск слов (компонент TListBox) обзовем lbWords.

Теперь напишем для наших кнопок обработчики событий:

procedure TSelfDemoForm.btnAddClick(Sender: TObject);
var wd:TWordSelfDemo;
begin
    wd:=TWordSelfDemo.Create;
    wd.Word:=edWord.Text;
    FWordsContainerSelfDemo.Add(wd);
end;

procedure TSelfDemoForm.btnDeleteClick(Sender: TObject);
begin
    if lbWords.ItemIndex>=0 then FWordsContainerSelfDemo.Delete(lbWords.ItemIndex);
end;

procedure TSelfDemoForm.btnEditClick(Sender: TObject);
begin
   if lbWords.ItemIndex>=0 then TWordSelfDemo(FWordsContainerSelfDemo[lbWords.ItemIndex]).Word:=edWord.Text;
end;

но это еще не все, нам нужно объявить в классе формы поле контейнера и написать обработчики события создания и удаления формы:

TSelfDemoForm = class(TForm)
   lbWords: TListBox;
   btnAdd: TButton;
   edWord: TEdit;
   btnDelete: TButton;
   btnEdit: TButton;
   procedure FormCreate(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
   procedure btnAddClick(Sender: TObject);
   procedure btnDeleteClick(Sender: TObject);
   procedure btnEditClick(Sender: TObject);
private
  FWordsContainerSelfDemo:TWordsContainerSelfDemo;
public
{ Public declarations }
end;
 
procedure TSelfDemoForm.btnDeleteClick(Sender: TObject);
begin
    if lbWords.ItemIndex>=0 then FWordsContainerSelfDemo.Delete(lbWords.ItemIndex);
end;

procedure TSelfDemoForm.btnEditClick(Sender: TObject);
begin
   if lbWords.ItemIndex>=0 then TWordSelfDemo(FWordsContainerSelfDemo[lbWords.ItemIndex]).Word:=edWord.Text;
end;

Все, теперь можно запустить пример (исходники можно скачать здесь):

TObject, демонстрация использования self (ClassType,TClass,self,TList,Pointer,Add, TClass)

 

И так, мы с вами поняли, что же такое указатель self. Теперь вернемся к нашему пресловутому TObject. Рассмотрим еще раз строку:

 Pointer(Result) := PPointer(Self)^;

Мы присваиваем результату функции значение, находящееся там, куда указывает self, при чем сам указатель мы преобразуем к PPointer, поскольку прямое присваивание вызовет у нас ошибку на этапе компиляции: Pointer type required (ожидается тип Pointer).

Что же у нас содержаться по адресу, на который указывает self? А там у нас находиться, собственно говоря, сам класс (TClass).

- Для чего его можно использовать на практике? - спросите вы.

- Допустим, - отвечу я, - вы хотите создать экземпляр класса неизвестного заранее типа. Вы передаёте в неё переменную классового типа и указатель на результат.

Практически в любой программе используется метод:

procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);

 где TComponentClass объявлен как

TComponentClass = class of TComponent;


Метод CreateForm применяется в главной части программы, для создания форм:

program ProjectTClass;

uses
Forms,
UnitTClass in 'UnitTClass.pas' {frmTClassDemo};

{$R *.res}

begin
     Application.Initialize;
     Application.CreateForm(TfrmTClassDemo, frmTClassDemo);
     Application.Run;
end.

Другой случай: в TTreeView Вы можете в обработчике события задать своего потомка TTreeNode, с дополнительными свойствами:

procedure TMyForm.tvMyTreeViewCreateNodeClass(Sender: TCustomTreeView;var NodeClass: TTreeNodeClass);
begin
     //некий код
end;

 Но это мы уже рассмотрим в следующем уроке.


Скриншоты, помеченные знаком * , являются цитатами и иллюстрациями  программного продукта "Delphi", авторское право на который принадлежит "Borland Software Corporation".


 

Последнее обновление ( 24.05.2013 г. )
 
« След.   Пред. »
 
© 2024 Программирование - это просто
Joomla! - свободное программное обеспечение, распространяемое по лицензии GNU/GPL.
Русская локализация © 2005-2008 Joom.Ru - Русский Дом Joomla!
Design by Mamboteam.com | Powered by Mambobanner.de
Я принимаю Яндекс.Деньги