Self — Переменная Delphi

В чем отличие между Create(Self) и Create(Application)?

Delphi , Компоненты и Классы , Процедуры и Функции

В чем отличие между Create(Self) и Create(Application)?

Self может быть использовано только в методе класса, и ссылается на текущий экземпляр класса. Таким образом «Self» в методе класса TForm1 ссылается на текущий экземпляр TForm1. При создании компонента Вы передаете его владельца (owner) в конструктор. При уничтожении формы или компонента автоматически уничтожаются и все компоненты владельцем которого она является. Таким образом если при создании формы передать в качестве владельца Application эта форма будет автоматически уничтожена при уничтожении Application. Если же при создании формы передать в качестве владельца другую форму, вновь созданная форма будет автоматически уничтоженна при уничтожении формы-владельца.

Статья В чем отличие между Create(Self) и Create(Application)? раздела Компоненты и Классы Процедуры и Функции может быть полезна для разработчиков на Delphi и FreePascal.

Комментарии и вопросы

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

Self — Переменная Delphi

Параметр Sender в Delphi-программе присутствует в каждом обработчике событий любого компонента. Однако, поскольку в использовании параметра Sender часто нет необходимости, новички про него «забывают» и часто даже не догадываются о его предназначении. В этой статье я хочу рассказать о том, для чего предназначен параметр Sender Delphi и как работать с таким, как оказывается, важным и удобным параметром как Sender.

Sender имеет тип TObject, и имеет значение объекта — источника события, в обработчике которого он используется. То есть, если на Форме находится несколько одинаковых компонентов, к тому же выполняющих одинаковые функции, то нет необходимости для каждого из них создавать свои процедуры-обработчики событий. Вполне достаточно одного комплекта обработчиков, а выяснить, к какому компоненту относится событие обрабатываемое именно сейчас, позволит именно параметр Sender.

Ну, во первых, мы можем непосредственно выяснить имя компонента — источника события. Положите на Форму два любых компонента (например кнопки Button), для одного из низ создайте заготовку обработчика любого события (например OnClick), а второму просто сопоставьте обработчик первого. Для этого откройте Инспектор Oбъектов, выберите второй ваш объект, и на вкадке Events щёлкните по обработчику выбранного события (OnClick в данном случае). В раскрывшемся списке выберите Button1Click. Затем щёлкните туда дважды. В редакторе кода, куда вы попадёте, введите такой код:

if Sender = Button1
then Caption:=’Щелчок по кнопке №1′
else Caption:=’Щелчок по кнопке №2′;

Программа покажет, по какой именно кнопке был щелчок. Ну а зная это, можно предусмотреть дальнейшую реакцию программы.

Это ещё не всё! Работая с параметром Sender, можно обойтись даже и без выяснения имени компонента-источника. Например, задача такая: мы должны следить за свойством Text нескольких компонентов Edit и при появлении в любом из них символа ‘,’ (запятая) менять его на ‘.’ (точка). Создайте такой обработчик события OnChange для одного из Edit’ов, а остальным просто сопоставьте, как в предыдущем случае:

Прежде всего заметим, что слово Edit1 написано много раз, что на самом деле излишне. С помощью оператора присоединения with избавимся от необходимости писать Edit1 внутри операторов:

Всё! Теперь для того чтобы избавится от запятых во всех Edit’ах, нужно заменить в операторе присоединения Edit1 на — лучше вот так, в скобках.
Дело в том, что конструкция (Sender as TObject) позволяет работать с источником события как с соответствующим объектом. В частности, если после (Sender as TEdit) поставить точку, то Delphi выдаст нам список свойств и методов компонента Edit. Хотя, по лично моей логике, вместо (Sender as TEdit) достаточно было бы просто Sender. Ан нет, не получается.

Теперь вы понимаете, почему мы избавились от Edit1 внутри операторов — заменять каждый Edit1 на было бы затруднительно!

Наконец, иногда бывает необходимо выяснить не имя, а тип источника события. В этом поможет оператор is , с помощью которого параметр Sender можно сравнить с одним из типов Delphi:

if (Sender is TButton)
then Caption:=’Источник события — кнопка TButton’;

Кстати, насчёт использованного выше способа замены запятой на точку. Вместо того чтобы заменять их в тексте строки, гораздо удобнее делать это прямо «на лету», используя процедуру OnKeyPress:

procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key=’,’ then Key:=’.’;
end;

Почему я сразу не использовал этот способ? Потому что тут вообще не используется параметр Sender! Хотя работать эта процедура также будет для всех компонентов, которым она сопоставлена. Но учитесь видеть разные возможности, когда-то и первый способ пригодится. И вообще, учитесь.

Обзор компонентов Delphi В начало урока Компонент Delphi SpeedButton

Николай, добавлено 21.04.12, 14:41:07
отличная статья, выручила. 5+ Ольга, добавлено 28.05.12, 23:07:55
если создаешь свой компонент Tlabel в кнопке. Созданный компонент становится невидимым если щелкаешь по нему 2 раза lb.OnDblClick:=label2.OnDblClick; а в Label2 пишем if sender is Tlabel then tlabel(sender).Visible:=false;
Как узнать при проверке какой из созданных компонентов был стерт.
Автор, добавлено 29.05.12, 05:45:27
При какой проверке? В этой же процедуре?

if Sender=Label2
then Caption:=’Был стёрт Label2′;
else Caption:=’Был стёрт lb’;

Я же это показал в статье, читали её? Сравните:

if Sender = Button1
then Caption:=’Щелчок по кнопке №1′
else Caption:=’Щелчок по кнопке №2′;
Ольга, добавлено 29.05.12, 11:40:00
Я это читала. Загвоздка в том что lb будет не один при каждом нажатии кнопки создается новый и если придавать каждому имя с цифрой или использовать tag все равно запоминается последний, а как сделать чтобы программа узнавала какой lb был стерт первый второй или например третий из созданных? Автор, добавлено 29.05.12, 12:08:37
Не понимаю. Sender указывает на источник, какая разница какой он по счёту? Выяснить что за компонент — не проблема. И даже через Tag, почему нет? А вы пробовали, или умозрительно сомневаетесь? Ольга, добавлено 29.05.12, 12:17:33
Пробовала. не получается. если было создано 5 компонентов lb потом стираю один из них и в Label2.caption хочу чтоб написал lb.tag стертого он пишет последний то есть 5 а не один или 2 который был стерт Автор, добавлено 29.05.12, 12:30:41
Давайте, скопируйте сюда процедуры создания компонентов и щелчка по ним, я скопирую себе и посмотрим вместе что не так. Автор, добавлено 29.05.12, 12:45:24
Отмена. Вообще не понимаю в чём проблема. Вот я не создавал, просто поставил 5 лабелей, каждому таг от 1 до 5. Процедура OnDblClick:

Caption:=IntToStr((Sender as TLabel).Tag); Ольга, добавлено 29.05.12, 12:47:06
<$R *.dfm>
var lb:Tlabel;b:boolean;i:byte;
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
lb:=tlabel.Create(self);
lb.Parent:=self;
lb.Font.Color:=clnavy;
lb.Font.Name:=’Arial’;
lb.Font.Size:=26;
lb.Caption:='(‘;
inc(i);
if b then lb.Tag:=i-1
else lb.Tag:=i;
lb.OnDblClick:=label1.OnDblClick;
lb.Left:= 20+random(100);
lb.Top:=20+random(100);

procedure TForm1.Label1DblClick(Sender: TObject);
begin
b:=true;
if sender is Tlabel then begin
tlabel(sender).Visible:=false;
label1.Caption:=inttostr(lb.tag);
end else label1.Caption:=’net’;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
b:=false;
i:=0;
end;

end.
Автор, добавлено 29.05.12, 13:02:46
Всё ясно. Проблема в том, что у вас все лабели создаются одной переменной, и фактически вы присваиваете таг не компоненту а этой переменной lb. Естественно, она и будет иметь таг последний. А исчезновение вы делаете не этой переменной, а как раз компонента типа TLabel — источника события, поэтому это получается. Ольга, добавлено 29.05.12, 13:05:54
А как присваивать таг компоненту в моем случае? Автор, добавлено 29.05.12, 13:21:02
Пойдём методом последовательного приближения
1. Присваивать нужно ведь не переменной а компоненту, который вы создали. А обращение к компоненту происходит ВСЕГДА с указанием его имени.
2. Или использовать для каждого компонента собственную переменную. Но и тогда вы будете использовать таг не компонента а переменной, просто они будут совпадать. Ольга, добавлено 29.05.12, 13:36:59
А по конкретнее можно? в моем случае мне менять создавать вектор переменных но и тогда будет запоминаться конечная переменная. помогите пожалуйста! Ольга, добавлено 29.05.12, 13:57:10
с вектором тоже не получается Ольга, добавлено 29.05.12, 14:24:45
как присваивать компоненту? lb из переменной после строк lb:=tlabel.Create(self);
lb.Parent:=self; становится компонентом логически?! или нет? и что делать? куда и как присваивать? Автор, добавлено 29.05.12, 15:14:18
Создаётся динамический массив — вы это называете вектором? Ну пусть.

var lb: Array of Tlabel;

Впрочем, вот вам весь проект. Только не копируйте слепо, а посмотрите в чём разница:

var
Form1: TForm1;
b:boolean;i:byte;
lb: Array of Tlabel;

procedure TForm1.Button1Click(Sender: TObject);
begin
SetLength(lb, i+1);
lb[i]:=tlabel.Create(Self);
lb[i].Parent:=Self;
lb[i].Font.Color:=clnavy;
lb[i].Font.Name:=’Arial’;
lb[i].Font.Size:=26;
lb[i].Caption:='(‘;
if b then lb[i].Tag:=i
else lb[i].Tag:=i+1;
lb[i].OnDblClick:=label1.OnDblClick;
lb[i].Left:= 20+random(100);
lb[i].Top:=20+random(100);
inc(i);
end;

procedure TForm1.Label1DblClick(Sender: TObject);
begin
b:=true;
if (sender is Tlabel) then
begin
tlabel(sender).Visible:=false;
label1.Caption:=inttostr(tlabel(sender).tag);
end else
label1.Caption:=’net’;
end;
Ольга, добавлено 29.05.12, 16:07:13
Спасибо!я не списывала тупо. в моем массиве были две ошибки и я их исправила! setlength(lb,10) на setlength(lb,i-1) но это не такая большая загвозка была в этом: label1.Caption:=inttostr(i-1);я афищировала только и label1.Caption:=inttostr(tlabel(sender).tag);
Спасибо большое! и извените за беспокойство! Ольга, добавлено 29.05.12, 16:33:04
кстати уничтожать компонент этим же способом не получается!или только у меня? выдает ошибку Access violation of adress.
if (sender is Tlabel) then tlabel(sender).Free; и на это тоже
if (sender is Tlabel) then freeandnil(tlabel(sender)); Автор, добавлено 29.05.12, 17:36:24
Да, что-то ничего пока не получается. Так что вам спасибо — буду думать. Как придумаю — допишу способ уничтожения к статье «Динамическое создание компонентов». А то там пока тоже Free, а это оказывается на компоненты созданные в массиве не действует. Вернее, действует — компонент уничтожается, но тут же появляется неустранимая ошибка. Ольга, добавлено 29.05.12, 17:51:26
Спасибо! Ваши статьи очень помогают! Автор, добавлено 29.05.12, 19:49:34
Касаемо уничтожения компонентов. Ошибка возникает потому, что «нельзя уничтожать компонент в его же событии» — цитирую ответ на программистском форуме. Поэтому берём таймер, передаём в него по щелчку параметр Tag, а по его срабатыванию разыскиваем на Форме компонент типа TLabel с Tag’ом равным переданному — и уничтожаем. Евгений, добавлено 1.06.12, 08:09:51
на сайте статьи очень полезные, но можете ли вы мне помочь в такой ситуации:
я создал динамически кнопки и у них есть порядковые номера(массив) вот как бы мне узнать индекс той кнопки на которую я нажал? что нужно наипсать для процедуры proc:

Илон Маск рекомендует:  Что такое код mailparse_uudecode_all

Блог GunSmoker-а

. when altering one’s mind becomes as easy as programming a computer, what does it mean to be human.

22 декабря 2008 г.

Новое ключевое слово static в Delphi

Недавно я переводил пост Почему методы класса должны быть помечены словом «static», чтобы их можно было использовать в качестве функции обратного вызова? Реймонда Чена. Там я оставил весь код «как есть» — на C++. Здесь я рассмотрю этот вопрос с точки зрения Delphi.

Как известно, в языке есть такие сущности как процедуры/функции и методы. Причём начинающие программисты часто путают эти два понятия.

Функция (в дальнейшем здесь будет также подразумеваться и процедура) — это код. Процедурная переменная — это указатель на код. Например: Метод — это тоже код, но код, связанный с классом. Указатель на метод — это ссылка на код + ссылка на конкретный объект. Например: Когда путают одно с другим компилятор чаще всего показывает такое сообщение: «Incompatible types: regular procedure and method pointer». Чаще всего или забывают писать «of object» в объявлении своих процедурных типов или пытаются передать в функцию (чаще всего как callback — т.е. функцию обратного вызова) метод класса вместо обычной функции (а самым упорным это иногда удаётся).

Что делает эти две сущности такими принципиально несовместимыми? Функция — это просто код. Она не имеет связи с данными, отличными от тех, что передаются в её параметры. Методы класса помимо работы с параметрами (как и обычная функция) ещё могут оперировать с данными объекта (вот оно: «код» vs «код + данные»), например: С функциями такое невозможно — обратите внимание, как вы манипулируете с P3 (он же: Self.P3) в методе. Собственно сам объект (это встроенная переменная Self) неявно передаётся в метод первым параметром. Поэтому, если метод объявлен как function(const P1, P2: Integer): Integer of object — с двумя параметрами, то, на самом деле, он трактуется как функция с тремя параметрами: function(Self: TSomeObj; const P1, P2: Integer): Integer . Именно это различие (на бинарном уровне) делает несовместимыми обычные функции и методы.

Соответственно, указатель на обычную функцию — это просто указатель (pointer), только что типизированный (это я про TDoSomethingFunc) — т.е. 4 байта. А вот указатель на метод — это уже запись или, если будет угодно, два указателя — один на код, второй — на данные, т.е. всего 8 байт.

Понятно, что если у вас что-то предназначено для передачи указателя на функцию, то никакими силами указатель на метод вы туда не пропихнёте. Хотя бы потому, что размер у них разный.

Ещё в Delphi есть классовые методы. Это такие методы, которые можно вызывать не имея на руках объект. В этом случае вместо объекта в неявный параметр Self передаётся информация о классе. Т.е. в классовых методах вы не можете использовать информацию о конкретном объекте (например, читать/писать его поля), но можете использовать информацию о классе — например, вызывать конструктор класса. Также методы класса могут быть виртуальными. Заметим, что сигнатура функции, реализующей метод, всё ещё совпадает с сигнатурой обычного метода: неявный параметр (данные класса вместо Self) + все явные параметры метода.

Например: Теперь ещё один шажок и мы переходим к тому, о чём говорил Реймонд Чен. Классовый метод можно объявить статическим (только в новых версиях Delphi). В этом случае у него не будет неявного параметра. Разумеется, при этом он не может использовать информацию экземпляра и класса. Зато он и не отличается от обычной функции.

Рассматривая пример с потоком, вот что мы могли бы написать в старых Delphi без поддержки статических классовых методов: Теперь, с введением нового ключевого слова static, появилась возможность писать так: При этом Реймонд говорит о том, что если у Execute сделать модель вызова stdcall, то бинарные сигнатуры параметра CreateThread, методов ThreadProc и Execute совпадут — поэтому, мол, умный компилятор уменьшит код ThreadProc до простого jmp. Увы, но компилятор Delphi не настолько умён — в этом случае он генерирует полный вызов вместе с передачей параметра.

Self — Переменная Delphi

Продолжаем наше обучение! В Delphi очень важную роль играют переменные. В процессе работы программы в переменных можно как хранить так и извлекать информацию. Переменные могут иметь разный тип. Например для того, чтобы в переменную записать какой-нибудь текст используется тип String. Для того, что бы записать в переменную число используют тип Integer.

Вот список основных типов переменных в Delphi:

  • Integer — целые числа из диапазона: -2147483648..+2147483647
  • Shortin — целые числа из диапазона: -128..+127
  • Byte — целые числа из диапазона: 0..+255
  • Real — как целые так и дробные числа из диапазона: 5e-324..1.7e+308
  • Double — схож с типом Real
  • String — строковый тип данных
  • Char — символьный тип данных
  • Bollean — логический тип данных. Может принимать значение True — истина или False — ложь

С теорией мы закончили, теперь давайте откроем Delphi 7 и создадим новый проект. После этого кидаем на форму знакомый нам компонент Button и еще не знакомый Label. Компонент Label эта такая полезная вещь, в которую можно записать какую-нибудь подпись. Например подпись для другого компонента или просто записать в него автора программы. Попробуйте отыскать компонент Label сами, наводя на все компоненты в вкладке Standard и читая всплывающую подсказку. Кому сложно найти, то это четвертый компонент слева, не считая значка курсора.

Я всё сделал и у меня получилось вот так:

Сейчас нам нужно создать событие OnClick на кнопке, я надеюсь, что вы помните, как это делать.
Переменные объявляются между ключевыми словами procedure и begin. Объявление начинается с ключевого слова var, потом пишется имя переменной и через двоеточие её тип. Заканчивается все как всегда точкой с запятой.

Создадим переменную S типа String в процедуре OnClick: После этого между ключевыми словами begin end присвоим переменной значение равное ‘Моя первая переменная’. Присвоение пишется следующим образом. Пишем имя переменной, оператор присвоения := и значение. Если мы записываем информацию типа String, то информация заключается в одинарные кавычки.

Общий вид: Теперь если скомпилировать программу и нажать на кнопку ничего существенного не произойдет, просто в переменную запишется значение и всё. Попробуем вывести значение из переменной. Делается это также просто как и записывается. Выводить значение мы будем в наш Label.

Синтаксис такой: Разберем этот код подробно. Сначала мы написали Label1, потом пишем точку и в Delphi появляется огромный список со свойствами данного компонента. Можно конечно порыться и отыскать там Caption, но мы будем умнее! Мы, после того как поставили точку, напишем еще букву C и Delphi как бы отсортирует все свойства и найдет все, которые начинаются с буквы C. Первым в списке как раз будет свойство Caption.

Выбираем его из списка и нажимаем на Enter. Заметьте, что мы писали Label1.C, но после того, как нажали Enter, Delphi сам дописал название свойства. Далее опять же идет оператор присвоения и наша переменная.

Вы наверняка спросите: «Зачем же переменная, если можно было написать Label1.Caption:=’Моя первая переменная’;?». Ответ простой. Это нужно затем, что мы изучаем переменные :).
Нет, на самом деле так присваивать тоже можно, но представьте такую ситуацию, что вы написали очень большую, популярную программу и у вас, там в программе, пятидесяти компонентам присваивается одно и тоже значение и вот перед вами встала задача: «Поменять это значение на более универсальное и понятное для пользователя».

Что вы будете делать?

  • В первом случае у вас всем этим компонентам присваивается одна и та же переменная и чтобы изменить всем этим пятидесяти компонентам значение вам просто нужно поменять значение в переменной.
  • Во втором случае вы сидите 20 минут и всё копируете и копируете значение всем пятидесяти компонентам.

Вывод делайте сами.

И так, продолжаем! В общем виде должно быть так: Компилируем нашу программу и нажимаем на Button (батон/кнопку). Сразу же компонент Label вместо Label1 будет показывать Моя первая переменная.

На этом хотелось бы закончить, так как я уже устал писать урок :), но я еще не познакомил вас с типом Integer и как присваивать переменную с таким типом. Вы думаете, что присваивать ее нужно точно так же как и переменную типа String, но вы ошибаетесь.
Дело в том, что свойству Caption вообще у всех компонентов можно присвоить только текстовые значения. Как мы будем присваивать числовой тип если можно только текстовой? Всё проще некуда. Между типами переменных можно как бы переключаться, то есть можно из числового типа сделать текстовой и присвоить его компоненту Label. Этим мы сейчас и займемся.

Для начала нужно начать сначала :). Объявим переменную с именем I и типом Integer, дописав ее к переменной S. Код: Далее присвоим переменной I значение 21. Заметьте, что числовое значение записывается без одинарных кавычек! Теперь присвоим свойству Caption значение переменной I, для этого нужно воспользоваться оператором IntToStr(). Он как бы конвертирует числовой тип в текстовой. В скобках указывается переменная, которую нужно конвертировать.

Общий вид кода: Скомпилируйте программу и вы увидите, что Label будет отображать значение переменной I, то есть 21.

Ну вот и всё! Удачи!
Встретимся в следующем уроке!

Источник: www.thedelphi.ru
Автор: Савельев Александр
Опубликовано: 22 Апреля 2012
Просмотров:

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

Self — Переменная Delphi

Что такое Self — в справке написано, что это вроде как собственник (Owner) объекта. Хотя чем он отличается от Parent — мне не понятно.
Но проблема не в этом.
Проблема в том, что это иногда не работает! Если прогу запускать в Delphi, то он выдаёт ошибку Access Violation (на строчке TStringGrid.Create(Self) ) и показывает CPU Window (вроде так его называют). Если запускать без Delphi, вообще программа завершается без предупреждений и ошибок! Даже try/except не прокатывает, всё равно завершается.

Илон Маск рекомендует:  Что такое код asp allowanonymous

Пришла в голову идея (видимо, на подсознательном уровне ) вместо Self написать nil. Тогда всё заработало! Почему? Чем они отличаются?

Ещё одну деталь хочу добавить к своим показаниям. Не уверен, но это может иметь отношение к делу.
Перед созданием StringGrid’а, в программе есть код, создающий динамический массив A
(array of byte). Опытным путём установлено, что будет строчка
TStringGrid.Create(Self)
работать или нет, зависит от устанавливаемой длины этого массива! Как такое может быть!?

приведи код полностью

.
Procedure TSomeObj.SomeMethod;
.
Begin
TempGr >End;

Интересует SomeObj(создан ли он на момент вызова SomeMethod)

Нашёл ошибку в своём коде.
Но всё равно не понимаю, почему возикает ошибка при создании StringGrid

А если написать for i:=1 to 3 то ошибки не будет
Так какой параметр лучше передавать Self или nil? Чем будет отличаться поведение созданных объектов одним и вторым способом?

Shaggy
Этот код находится в событии FormActivate, форма создаётся автоматически. Поэтому SomeObj существует.

Makarov
Что значит «владеть» ?

Но всё равно не понимаю, почему возикает ошибка при создании StringGrid

SetLength(A, 4);
for i:=1 to 4 do A[i]:= i; //ошибка: нужно for i:=1 to 3

tempGr >
А если написать for i:=1 to 3 то ошибки не будет
Так какой параметр лучше передавать Self или nil? Чем будет отличаться поведение созданных объектов одним и вторым способом?

Судя по всему, устанавливая эту самую длину, ты вылезаешь за его границу и «наступаешь» на адреса других переменных — портишь Self. Вообще же такие ошибки очень не предсказуемы, при одном расположении звезд может отработать без ошибок, ошибка может вывалиться сразу или через полчаса. Как повезет © trainer.

Self — это текущий объект, метод которого выполняется. Аналог С++ -ного this. В данном случае, насколько я понял, это форма. Т.е. ты при создании обекта назначаешь его собственником (Owner) форму. У всех компонентов или их наследников есть (если не nil) собственники — связь Owner/TComponent, отчечающая за «автоматическое» удаление компонентов. При создании компонент помещается в список Components компонента-собственника, при удалении компонента он удаляется из этого списка. Все компоненты из этого списка удаляются при удалении собственника — можно сказать, автоматом.
Если при создании компонета указать ему Owner как nil, то нужно самому удалять этот компонент через Free.

Связь же Parent/TControl приблизительно соответсвует иерархии окон виндоуз. Родитель контрола — это компонент, содержащий окно, являющееся родительским (Parent Window) по отношению к окну дочернего контрола (именно поэтому Рarent — это TWinControl, т.е. контрол с HWND).
Если дочерний контрол не оконный, типа TLabel, то для него VCL «эмулирует» оконное поведение — например, он не может вылезать за пределы окна родительского контрола.
Все дочерние контролы хранятся в списке Controls родительского контрола. Они добавляются/удаляются оттуда при изменении св-ва Parent контрола. Так же как и для Owner/TComponent, все контролы из Controls удаляются при удалении родительского контрола.

Why is Self assignable in Delphi?

This code in a GUI application compiles and runs:

(tested with Delphi 6 and 2009)

  • why is Self writable and not read-only?
  • in which situations could this be useful?
  • is this also possible in Delphi Prism? (I think yes it is, see here)

Update: Delphi applications/libraries which make use of Self assignment:

5 Answers 5

That’s not as bad as it could be. I just tested it in Delphi 2009, and it would seem that, while the Self parameter doesn’t use const semantics, which you seem to be implying it should, it also doesn’t use var semantics, so you can change it all you want within your method without actually losing the reference the caller holds to your object. That would be a very bad thing.

As for the reason why, one of two answers. Either a simple oversight, or what Marco suggested: to allow you to pass Self to a var parameter.

Maybe to allow passing to const or var parameters?

It could be an artefact, since system doesn’t have self anywhere on the left of := sign.

Assigning to Self is so illogical and useless that this ‘feature’ is probably an oversight. And as with assignable constants, it’s not always easy to correct such problems.

The simple advice here is: don’t do it.

In reality, «Self» is just a name reference to a place on the stack that store address pointing to object in the heap. Forcing read-only on this variable is possible, apparently the designer decided not to. I believe the decision is arbitrary.

Can’t see any case where this is useful, that’d merely change a value in stack. Also, changing this value can be dangerous as there is no guarantee that the behavior of the code that reference instance’s member will be consistence across compiler versions.

Updated: In response to PatrickvL comment

The ‘variable’ «Self» is not on the stack (to my knowledge, it never is); Instead it’s value is put in a register (EAX to be exact) just before a call to any object method is made. –

Nope, Self has actual address on the memory. Try this code to see for yourself.

Sometimes, when you want to optimize a method for as far as you can take it (without resorting to assembly), ‘Self’ can be (ab)used as a ‘free’ variable — it could just mean the difference between using stack and using registers.

Sure, the contents of the stack are most probably already present in the CPU cache, so it should be fast to access, but registers are even faster still.

As a sidenote : I’m still missing the days when I was programming on the Amiga’s Motorola 68000 and had the luxury of 16 data and 16 address registers. I can’t believe the world chose to go with the limited 4 registers of the 80×86 line of processors!

And as a final note, I choose to use Self sometimes, as the Delphi’s optimizer is, well, not optimizing that well, actually. (At least, it pales compared to what trickery one can find in the various LLVM optimizers for example.) IMHO, and YMMV of course.

Delphi-Help

Self

Описание

var Self : Class Type;

Переменная Self скрытый параметр для каждого метода в объекте. Он позволяет методу обратиться к объекту.

Это особенно важно, когда параметры метода такие же, как переменные класса, предназначенные для содержания этих переданных значений.

Пример кода

// Полный код Модуля.
// ————————————————————
// Вы должны поместить этот код в модуль с именем Unit1 и с формой
// названную Form1, которая имеет событие OnCreate названное FormCreate.

unit Unit1;

interface

uses
Forms, Dialogs, Classes, Controls, StdCtrls, Windows;

type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
end;

var
Form1: TForm1;

implementation
<$R *.dfm>// Вложение определений формы

procedure TForm1.FormCreate(Sender: TObject);
begin
// Использование Self для идентификации формы позволяя нам
// установить некоторые атрибуты формы
Self .Caption := ‘Test program’;
Self .Visible := True;
end;

События

Программистам, давно работающим в Windows, наверное, не нужно пояснять смысл термина «событие». Сама эта среда и написанные для нее программы управляются событиями, возникающими в результате воздействий пользователя, аппаратуры компьютера или других программ. Весточка о наступлении события — это сообщение Windows, полученное так называемой функцией окна. Таких сообщений сотни, и, по большому счету, написать программу для Windows — значит определить и описать реакцию на некоторые из них.

Работать с таким количеством сообщений, даже имея под рукой справочник, нелегко. Поэтому одним из больших преимуществ Delphi является то, что программист избавлен от необходимости работать с сообщениями Windows (хотя такая возможность у него есть). Типовых событий в Delphi — не более двух десятков, и все они имеют простую интерпретацию, не требующую глубоких знаний среды.

Рассмотрим, как реализованы события на уровне языка Object Pascal. Событие — это свойство процедурного типа, предназначенное для создания пользовательской реакции на то или иное входное воздействие:

property OnMyEvent: TMyEvent read FOnMyEvent write FOnMyEvent;

Здесь FOnMyEvent — поле процедурного типа, содержащее адрес некоторого метода. Присвоить такому свойству значение — значит указать объекту адрес метода, который будет вызываться в момент наступления события. Такие методы называют обработчиками событий. Например, запись:

означает, что при активизации объекта Application (так называется объект, соответствующий работающему приложению) будет вызван метод-обработчик MyActivatingMethod .

Внутри библиотеки времени выполнения Delphi вызовы обработчиков событий находятся в методах, обрабатывающих сообщения Windows. Выполнив необходимые действия, этот метод проверяет, известен ли адрес обработчика, и, если это так, вызывает его:

if Assigned(FOnMyEvent) then FOnMyEvent(Self);

События имеют разное количество и тип параметров в зависимости от происхождения и предназначения. Общим для всех является параметр sender — он указывает на объект-источник события. Самый простой тип — TNotifyEvent — не имеет других параметров:

TNotifyEvent = procedure (Sender: TObject) of object;

Тип метода, предназначенный для извещения о нажатии клавиши, предусматривает передачу программисту кода этой клавиши о передвижении мыши — ее текущих координат и т. п. Все события в Delphi принято предварять префиксом On: onCreate, onMouseMove, onPaint и т. д. Дважды щелкнув в Инспекторе объектов на странице Events в поле любого события, вы получите в программе заготовку метода нужного типа. При этом его имя будет состоять из имени текущего компонента и имени события (без префикса On), а относиться он будет к текущей форме. Пусть, например, на форме Forml есть текст Label1 . Тогда для обработки щелчка мышью (событие O nclick ) будет создана заготовка метода TFormi.Label1click:

procedure TForml.LabellClick(Sender: TObject);

Поскольку события — это свойства объекта, их значения можно изменять в любой момент во время выполнения программы. Эта замечательная возможность называется делегированием. Можно в любой момент взять способы реакции на события у одного объекта и присвоить (делегировать) их другому:

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

Илон Маск рекомендует:  Защита e-mail от спам-ботов

Но какой механизм позволяет подменять обработчики, ведь это не просто процедуры, а методы? Здесь как нельзя кстати приходится существующее в Object Pascal понятие указателя на метод. Отличие метода от процедуры состоит в том, что помимо явно описанных параметров методу всегда неявно передается еще и указатель на вызвавший его экземпляр класса (переменная self ). Вы можете описать процедурный тип, который будет совместим по присваиванию с методом (т. е. предусматривать получение self ). Для этого в описание процедуры нужно добавить зарезервированные слова of object . Указатель на метод — это указатель на такую процедуру.

TMyEvent = procedure(Sender: TObject; var AValue: Integer) of object;

property OnMyEvent: TMyEvent read FOnMyEvent write FOnMyEvent;

procedure SetValuel(Sender: TObject; var AValue: Integer);

procedure SetValue2(Sender: TObject; var AValue: Integer);

Этот пример показывает, что при делегировании можно присваивать методы других классов. Здесь обработчиком события OnMyEvent объекта Objl по очереди выступают методы SetValuel и Setvaiue2 объекта Obj2 .

Обработчики событий нельзя сделать просто процедурами — они обязательно должны быть чьими-то методами. Но их можно «отдать» какому-либо другому объекту. Более того, для этих целей можно описать и создать специальный объект. Его единственное предназначение — быть носителем методов, которые затем делегируются другим объектам. Разумеется, такой объект надо не забыть создать до использования его методов, а в конце — уничтожить. Можно и не делать этого, объявив методы методами класса, о которых речь пойдет в одном из последующих разделов.

Мы сейчас решили задачу использования нескольких разных обработчиков того или иного события для одного объекта. Но не менее часто требуется решить обратную задачу — а как использовать для различных событий разных объектов один и тот же обработчик?

Если никакой «персонификации» объекта, вызвавшего метод, не нужно, все делается тривиально и проблемы не возникает. Самый простой пример: в современных программах основные функции дублируются дважды — в меню и на панели инструментов. Естественно, сначала нужно создать и наполнить метод содержимым (скажем, для пункта меню), а затем в Инспекторе объектов указать его же для кнопки панели инструментов.

Более сложный случай, когда внутри такого метода нужно разобраться, кто собственно его вызвал. Если потенциальные кандидаты имеют разный объектный тип (как в предыдущем абзаце — кнопка и пункт меню), то именно объектный тип можно применить в качестве критерия:

If Sender is TMenuItem then ShowMessage(‘Выбран пункт меню’);

Если же все объекты, разделяющие между собой один обработчик события, относятся к одному классу, то приходится прибегать к дополнительным ухищрениям. Типовой прием — использовать свойство Tag , которое имеется у всех компонентов, и, вполне вероятно, именно для этого и задумывалось:

TColor = clWhite,clRed,clBlue,clYellow,clAqua,clGreen, clMaroon,clBlack);

procedure TForml.CheckBoxClick(Sender: TObject);

with TCheckBox(Sender) do

then Color := Colors[Tag]

else Color := clBtnFace;

Пусть в форме имеется несколько переключателей. Для того чтобы при нажатии каждый из них окрашивался в свой цвет, нужно в Инспекторе объектов присвоить свойству Tag значения от 0 до 7 и для каждого связать событие onclick с методом CheckBoxClick . Этот единственный метод справится с задачей для всех переключателей.

НОВОСТИ ФОРУМА
Рыцари теории эфира
01.10.2020 — 05:20: ВОСПИТАНИЕ, ПРОСВЕЩЕНИЕ, ОБРАЗОВАНИЕ — Upbringing, Inlightening, Education ->
[center][Youtube]69vJGqDENq4[/Youtube][/center]
[center]14:36[/center]
Osievskii Global News
29 сент. Отправлено 05:20, 01.10.2020 г.’ target=_top>Просвещение от Вячеслава Осиевского — Карим_Хайдаров.
30.09.2020 — 12:51: ВОСПИТАНИЕ, ПРОСВЕЩЕНИЕ, ОБРАЗОВАНИЕ — Upbringing, Inlightening, Education ->
[center][Ok]376309070[/Ok][/center]
[center]11:03[/center] Отправлено 12:51, 30.09.2020 г.’ target=_top>Просвещение от Дэйвида Дюка — Карим_Хайдаров.
30.09.2020 — 11:53: ВОСПИТАНИЕ, ПРОСВЕЩЕНИЕ, ОБРАЗОВАНИЕ — Upbringing, Inlightening, Education ->
[center][Youtube]VVQv1EzDTtY[/Youtube][/center]
[center]10:43[/center]

интервью Раввина Борода https://cursorinfo.co.il/all-news/rav.
мой телеграмм https://t.me/peshekhonovandrei
мой твиттер https://twitter.com/Andrey54708595
мой инстаграм https://www.instagram.com/andreipeshekhonow/

[b]Мой комментарий:
Андрей спрашивает: Краснодарская синагога — это что, военный объект?
— Да, военный, потому что имеет разрешение от Росатома на манипуляции с радиоактивными веществами, а также иными веществами, опасными в отношении массового поражения. Именно это было выявлено группой краснодарцев во главе с Мариной Мелиховой.

[center][Youtube]CLegyQkMkyw[/Youtube][/center]
[center]10:22 [/center]

Доминико Риккарди: Россию ждёт страшное будущее (хотелки ЦРУ):
https://tainy.net/22686-predskazaniya-dominika-rikardi-o-budushhem-rossii-sdelannye-v-2000-godu.html

Завещание Алена Даллеса / Разработка ЦРУ (запрещено к ознакомлению Роскомнадзором = Жид-над-рус-надзором)
http://av-inf.blogspot.com/2013/12/dalles.html

[center][b]Сон разума народа России [/center]

[center][Youtube]CLegyQkMkyw[/Youtube][/center]
[center]10:22 [/center]

Доминико Риккарди: Россию ждёт страшное будущее (хотелки ЦРУ):
https://tainy.net/22686-predskazaniya-dominika-rikardi-o-budushhem-rossii-sdelannye-v-2000-godu.html

Завещание Алена Даллеса / Разработка ЦРУ (запрещено к ознакомлению Роскомнадзором = Жид-над-рус-надзором)
http://av-inf.blogspot.com/2013/12/dalles.html

[center][b]Сон разума народа России [/center]

Self — Переменная Delphi

Продолжаем наше обучение! В Delphi очень важную роль играют переменные. В процессе работы программы в переменных можно как хранить так и извлекать информацию. Переменные могут иметь разный тип. Например для того, чтобы в переменную записать какой-нибудь текст используется тип String. Для того, что бы записать в переменную число используют тип Integer.

Вот список основных типов переменных в Delphi:

  • Integer — целые числа из диапазона: -2147483648..+2147483647
  • Shortin — целые числа из диапазона: -128..+127
  • Byte — целые числа из диапазона: 0..+255
  • Real — как целые так и дробные числа из диапазона: 5e-324..1.7e+308
  • Double — схож с типом Real
  • String — строковый тип данных
  • Char — символьный тип данных
  • Bollean — логический тип данных. Может принимать значение True — истина или False — ложь

С теорией мы закончили, теперь давайте откроем Delphi 7 и создадим новый проект. После этого кидаем на форму знакомый нам компонент Button и еще не знакомый Label. Компонент Label эта такая полезная вещь, в которую можно записать какую-нибудь подпись. Например подпись для другого компонента или просто записать в него автора программы. Попробуйте отыскать компонент Label сами, наводя на все компоненты в вкладке Standard и читая всплывающую подсказку. Кому сложно найти, то это четвертый компонент слева, не считая значка курсора.

Я всё сделал и у меня получилось вот так:

Сейчас нам нужно создать событие OnClick на кнопке, я надеюсь, что вы помните, как это делать.
Переменные объявляются между ключевыми словами procedure и begin. Объявление начинается с ключевого слова var, потом пишется имя переменной и через двоеточие её тип. Заканчивается все как всегда точкой с запятой.

Создадим переменную S типа String в процедуре OnClick: После этого между ключевыми словами begin end присвоим переменной значение равное ‘Моя первая переменная’. Присвоение пишется следующим образом. Пишем имя переменной, оператор присвоения := и значение. Если мы записываем информацию типа String, то информация заключается в одинарные кавычки.

Общий вид: Теперь если скомпилировать программу и нажать на кнопку ничего существенного не произойдет, просто в переменную запишется значение и всё. Попробуем вывести значение из переменной. Делается это также просто как и записывается. Выводить значение мы будем в наш Label.

Синтаксис такой: Разберем этот код подробно. Сначала мы написали Label1, потом пишем точку и в Delphi появляется огромный список со свойствами данного компонента. Можно конечно порыться и отыскать там Caption, но мы будем умнее! Мы, после того как поставили точку, напишем еще букву C и Delphi как бы отсортирует все свойства и найдет все, которые начинаются с буквы C. Первым в списке как раз будет свойство Caption.

Выбираем его из списка и нажимаем на Enter. Заметьте, что мы писали Label1.C, но после того, как нажали Enter, Delphi сам дописал название свойства. Далее опять же идет оператор присвоения и наша переменная.

Вы наверняка спросите: «Зачем же переменная, если можно было написать Label1.Caption:=’Моя первая переменная’;?». Ответ простой. Это нужно затем, что мы изучаем переменные :).
Нет, на самом деле так присваивать тоже можно, но представьте такую ситуацию, что вы написали очень большую, популярную программу и у вас, там в программе, пятидесяти компонентам присваивается одно и тоже значение и вот перед вами встала задача: «Поменять это значение на более универсальное и понятное для пользователя».

Что вы будете делать?

  • В первом случае у вас всем этим компонентам присваивается одна и та же переменная и чтобы изменить всем этим пятидесяти компонентам значение вам просто нужно поменять значение в переменной.
  • Во втором случае вы сидите 20 минут и всё копируете и копируете значение всем пятидесяти компонентам.

Вывод делайте сами.

И так, продолжаем! В общем виде должно быть так: Компилируем нашу программу и нажимаем на Button (батон/кнопку). Сразу же компонент Label вместо Label1 будет показывать Моя первая переменная.

На этом хотелось бы закончить, так как я уже устал писать урок :), но я еще не познакомил вас с типом Integer и как присваивать переменную с таким типом. Вы думаете, что присваивать ее нужно точно так же как и переменную типа String, но вы ошибаетесь.
Дело в том, что свойству Caption вообще у всех компонентов можно присвоить только текстовые значения. Как мы будем присваивать числовой тип если можно только текстовой? Всё проще некуда. Между типами переменных можно как бы переключаться, то есть можно из числового типа сделать текстовой и присвоить его компоненту Label. Этим мы сейчас и займемся.

Для начала нужно начать сначала :). Объявим переменную с именем I и типом Integer, дописав ее к переменной S. Код: Далее присвоим переменной I значение 21. Заметьте, что числовое значение записывается без одинарных кавычек! Теперь присвоим свойству Caption значение переменной I, для этого нужно воспользоваться оператором IntToStr(). Он как бы конвертирует числовой тип в текстовой. В скобках указывается переменная, которую нужно конвертировать.

Общий вид кода: Скомпилируйте программу и вы увидите, что Label будет отображать значение переменной I, то есть 21.

Ну вот и всё! Удачи!
Встретимся в следующем уроке!

Источник: www.thedelphi.ru
Автор: Савельев Александр
Опубликовано: 22 Апреля 2012
Просмотров:

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

Delphi/Переменные

Переменная — область оперативной памяти, в которой лежит какое-то значение. Основные типы переменных в Delphi:

  • Integer — целые числа.
  • Real — целые и дробные числа.
  • Boolean — логический тип.
  • Char — символьный тип данных.
  • String — строковой тип данных.

Переменные указываются после ключевого слова var (variable — переменная). Общий вид указывания переменных:

В Delphi есть оператор присваивания — := .Общий вид присваивания:

Но с типом string и char, особое дело. Общий вид присваивания с типом string и char:

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL