 |  | |  | |
Self, Sender и events - ЧаВо | Полный текст материала
Другие публикации автора: Levko
Цитата или краткий комментарий: «... Идея написать эту статью возникла у меня после появления на Круглом столе очередного вопроса вида "У меня есть компонент, у него есть событие On... Что ему нужно присвоить чтоб оно работало?" ...» |
Важно:- Страница предназначена для обсуждения материала, его содержания, полезности, соответствия действительности и так далее. Смысл не в разборке, а в приближении к истине :о) и пользе для всех.
- Любые другие сообщения или вопросы, а так же личные эмоции в адрес авторов и полемика, не относящаяся к теме обсуждаемого материала, будут удаляться без предупреждения авторов, дабы не мешать жителям нормально общаться.
- При голосовании учитывайте уровень, на который расчитан материал. "Интересность и полезность" имеет смысл оценивать относительно того, кому именно предназначался материал.
- Размер одного сообщений не должен превышать 5К. Если Вам нужно сказать больше, сделайте это за два раза. Или, что в данной ситуации правильнее, напишите свою статью.
Всегда легче осудить сделанное, нежели сделать самому. Поэтому, пожалуйста, соблюдайте правила Королевства и уважайте друг друга.
Добавить свое мнение.
| | Содержит полезные и(или) интересные сведения | [1] | 5 | 50% | | | | Ничего особенно нового и интересного | [2] | 2 | 20% | | | | Написано неверно (обязательно укажите почему) | [3] | 3 | 30% | | Всего проголосовали: 10 | | | Все понятно, материал читается легко | [1] | 3 | 25% | | | | Есть неясности в изложении | [2] | 4 | 33.3% | | | | Непонятно написано, трудно читается | [3] | 5 | 41.7% | | Всего проголосовали: 12 |
[Обработчики событий] [Процедуры и функции] [Методы]
Отслеживать это обсуждение 
Всего сообщений: 913-08-2009 12:02Прочел, статья отличная! Примеры толковые, положил в свою копилку знаний! ))) |
|
28-07-2009 10:27Какая-то каша из-за непонимания ООП и притягивания за уши кривых решений.
Я бы рекомендовал автору изучить книжку "Паттерны проектирования" банды четырёх, чтобы его советы не вели "новичков" в заведомо неправильном направлении при создании приложений. |
|
23-07-2009 18:27>>> Но есть в этом способе одна небольшая неудобность. И вот какая. Наш подставной класс не несет никакой смысловой нагрузки (ведь мы его таким спроектировали); более того, скорее всего он будет содержать кучу методов для обработки самых разных событий множества различных обьектов.
Можно:
1. Вынести часто используемые обработчики в отдельный модуль.
2. Сгруппировать логически связанные обработчики по отдельным классам. Появляется смысловая нагрузка для класса: разбивка обработчиков на логически связанные группы.
>>> Плюс при инициализации нужно создать хотя бы один экземпляр этого класса, при завершении работы — не забыть уничтожить.
Не надо ни создавать, ни уничтожать. В некотором приближении метод можно рассматривать как совершенно отдельную функцию, и эта функция никак не связана с объектом, а знает о своём экземпляре класса только через параметр Self. Если Self внутри этой функции не используется, то функции совершенно безразлично, существует "её" объект или нет.
>>> И ради чего всё это? Ради одного факта приналежности процедуры обьекту?
А разве этого мало? Кроме того, мы уже выяснили, что эта принадлежность - чисто формальное явление и объект не надо ни создвавать, ни уничтожать...
>>> Возникает желание искать другие пути написания обработчиков. Я предлагаю еще два. Первый. Если Self нам не нужен, тогда самое время вспомнить о методах класса. Их можно вызывать, не создавая экземпляра класса, и в тоже время они пренадлежат классу.
Начнём с того, что в классовый метод передаётся не ссылка на объект, а ссылка на класс. А поскольку нам и так не надо ничего ни создавать, ни уничтодать, то зачем наводить допольнительную тень на плетень?
Способ второй. Мы уже знаем, что обычная процедура и метод отличаются отсутсвием или наличием скрытого параметра Self. А что, если определить его в статической процедуре?
procedure MyEvent(Self, Sender: TObject; Param: string);
<...>
Но не тут-то было. Любые попытки назначить эту процедуру в качестве обработчика вызывали отчаяное сопротивление компилятора в виде Invalid typecast.
Ну ещё бы... У них же даже размер разный...
Указатель на обычную процедуру - 4
Указатель на метод - 8
Почему? А потому, что указатель на метод, кроме собственно указателя на функцию, содержит ещё и указатель на объект... Тот самый, который будет передаваться в этот метод в качестве параметра Self... Проверить очень легко:
type
P1 = procedure;
P2 = procedure of object;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(SizeOf(P1)));
ShowMessage(IntToStr(SizeOf(P2)));
end;
>>> Но решение и здесь оказалось тривиальным:
>>> @t.OnEvent:=@MyEvent;
Ну, приложив достаточную силу, можно ещё и не такое сотворить...
>>> И наконец рекомендую использовать его только когда совсем нет возможности сделать процедуру методом, т.к. он немного противоречит концепциям, закладенным в ООП.
Ну, это уже лучше... Но моё предложение более конкретно: при назначении внешних обработчиков всегда делать обработчик обычным методом класса и не использовать внутри него Self.
Пример:
unit Edit_Util;
interface
uses StdCtrls, Graphics;
type
TEditEvents = class
public
procedure OnEditEnter(Sender: TObject);
procedure OnEditExit(Sender: TObject);
end;
implementation
procedure TEditEvents.OnEditEnter(Sender: TObject);
begin
(Sender as TEdit).Color := clLime;
end;
procedure TEditEvents.OnEditExit(Sender: TObject);
begin
(Sender as TEdit).Color := clWindow;
end;
end. Использование:
implementation
uses Edit_Util;
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
begin
for i := 0 to ControlCount - 1 do
if Controls[i] is TEdit then
with Controls[i] as TEdit do
begin
OnEnter := TEditEvents(nil).OnEditEnter;
OnExit := TEditEvents(nil).OnEditExit;
end;
end; |
|
23-07-2009 14:18Молодец Левко!!!
Отличная статья, так держать! |
|
22-07-2009 06:57>>> CTestClass = class(TObject)
Классы в Delphi принято называть с буквы T. Нет, компилятору конечно все-равно, ему хоть _dSA2fJ21_uIIow подставь, но только будет ли с этого кому-нибудь польза? Для некоторых случаем имеет смысл отступать от этой нормы (исключения, например, начинаются с буквы E), но такой подход неприемлем для статьи для новичков.
>>> Самые любопытные лезли в недра компонента
Новички, которые задают вопросы? Лезли в исходники VCL? Не смешите мои подковы (или что там у змей вместо подков)! Они даже у Гугля стесняются спросить.
>>> Впишем в тело DoEvent строку writeln(Self.FText), а в тело программы после создания t строки
А нельзя было сразу написать готовый код, а не напрягать извилину (я не описался - извилину) и думать, а не просто заниматься копипастой.
>>> поставим на вторую строку брейкпойнт
Я тут с одним, особо продвинутым программистом общался по Аське... от фразы "поставь брейкпойнт на строку 71" его стало рвать совершенно невменяемыми фразами. После некоторого времени (в том числе наблюдений за его действиями удаленно) я понял, что он просто не знает, что такое брейкпойнт.
>>> открываем View->Debug Windows->CPU (Ctrl+Alt+C)
Для новичков - поподробнее, что такое регистры. И что там еще за цикверки и буковки непонятные написаны. Ах да, если новичок продвинутый (нонсенс?) он вспомнит, что параметры передаются через стек (кстати, что это такое?). Про модели вызова ничего не сказано. Да, найти в справке их не представляет сложности (для меня), но для некоторых является проблемой даже понять, ГДЕ надо объявлять переменные в программе на Паскале.
>>> поэтому он может спокойно использовать Self внутри методов
Надо еще уточнить - использовать Self явно в большинстве случаев НЕ НУЖНО! Потому что за... надоели программы, где "затуманенно" написаны вещи типа Self.Left:=Self.Left+10;
>>> полям и другим методам класса-владельца вызванного метода
Можно вопрос - что означает "класс-владелец" в этом контексте?
>>> здесь же содержится первый, классический, вариант назначения событию собственного обработчика
Классический вариант (на мой, сугубо любительский взгляд) - это кинуть компонент на форму, ткнуть дважды в нужный ивент в Object Inspector и набить в открывшемся редакторе кода ShowMessage('Hello, world!');
>>> @t.OnEvent:=@MyEvent;
Ой, плохому учите. Тем более, новичков. Понятно, что когда я пользуюсь объявлением procedure ThreadProc(var Data:TThreadData); где TThreadData=record...end; и потом запускаю поток через BeginThread(...,@ThreadProc,...);, то я хотя бы понимаю, чем это может грозить. А вот новички, они дров наломают.
>>> только когда совсем нет возможности сделать процедуру методом
Можно вопрос, при КАКИХ условиях такой возможности не будет. Ну никак не могу придумать. Честно! |
|
22-07-2009 06:21Статья неплохая, но не для новичков, (статические методы, работа с указателями) и вообще, зачем учить молодеж плохому ;), последний пример вообще... идеологически некорректный чтоли? ИМХО конечно. В исходники неплохо-бы комментарии. Хотя, в целом прикольно... |
|
20-07-2009 08:08Статья - не для новичков. Стиль изложения не очень удачный, хотя сам материал довольно интересен - кто не знал, узнает, кто знал - вспомнит. |
|
20-07-2009 06:34Не совсем понятно, на какой, собственно, вопрос новичков отвечает столь непростая статья. Отсюда и сложности восприятия.
На вопрос про "Self, Sender, Event" я бы отвечал диаграммами и простыми примерами про ТГрузовик=class(ТАвтомобиль), а не отладчиком и указателями.
|
|
20-07-2009 06:20Для новичков консоль с событиями!?
На вопрос как вызвать отвечать: так и вызывать OnClick(Self). |
|
|
|