Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Корректно ли такое приведение типов?


Страницы:   1 2

kull ©   (2002-06-20 00:27:00) 

Привет народ!
Наткнулся на следующую проблемму:

В мою процедуру передается некий контрол,о котором я знаю только что он - TWinControl. И мне надо присвоить protected свойству нужное значение. Но так как свойство protected на прямую к нему не доступиться. Так вот: корректно ли следующее приведение типов?


type
TMyWinControl = class(TWinControl)
end;

procedure TForm1.MyClick(Sender: TObject);
begin
Beep;
end;

procedure TForm1.SetHandlers(AWinControl: TWinControl);
begin
//можно ли так доступаться к protected свойству?
TMyWinControl(AWinControl).OnClick := MyClick;
end;


Проект компиляется и работает, но правильно ли так делать и не грозит ли это глюками в дальнейшем?


Юрий Зотов   (2002-06-21 02:09:00) [1]

Пара замечаний к уже сказанному.

1. Такой способ получения доступа к protected-секции - это вполне законный прием и он действительно используется в самой VCL. Только слово "end" писать не нужно(хотя это и не ощибка) - вполне достаточно просто:

type
TMyWinControl = class(TWinControl);

2. А вот назначение обработчика
TMyWinControl(AWinControl).OnClick := MyClick;
я бы тоже назвал ОЧЕНЬ опасным приемом и ОЧЕНЬ опасным стилем. Таким способом не только затирается предыдущий обработчик, но и есть большой риск, что назначенный Вами тоже будет перезатерт. Как в итоге будет работать программа - непредсказуемо.


kull   (2002-06-20 20:50:00) [2]

Спасибо, Народ!


eheyler   (2002-06-20 19:23:00) [3]

Дело в том, что в паскале нельзя понизить видимость свойств,
и если св-во объявлено как published, то обратно его уже не
уберёшь. Поэтому в Дельфи принята практика Custom-конмпонентов, когда в Custom-предке все св-ва защищённые, а в потомках открываются те, которые нужны.

Из-за этого возникает вобщем-то неправильная с точки зрения ООП ситуация, когда несколько разных объектов наследуют св-во от одного предка, но к нему нельзя иметь доступ единообразно, приведя их к предку.

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


kull   (2002-06-20 18:53:00) [4]


> nick_sniper © (20.06.02 18:39)


Да согласен.

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

Так вот для каждоко такого компонента надо писать типа:

AControl.OnClick := MyOnClick;
и еще по другим событиям OnDragOver, OnDragDrop и т.д.


Но если наследовать этот Panel и переопределить так, чтобы при создании на нем контрола автоматически прописывались обработчики, то было бы очень удобно.


nick_sniper   (2002-06-20 18:39:00) [5]

2kull
Разница определенно есть. Фактически ты описал два наследника TObject, т.е. аналогично будет:
type
TClass1 = class(TObject)
end;

TClass2 = class(TObject)
end;

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

Я наверное перестарался с запугиванием. Приношу свои извинения. Но ведь не зря же Borland из версии в версию прячет все поля в WinControle. Может быть для некоторых компонентов абсолютно противопоказан обработчик Onclick ? И пользователь не должен иметь возможности его вводить? Такая ситуация может запросто встретиться. А ты его в своей процедуре случайно установишь? Видишь какую дискуссию создал и в какие дебри программирования ноарод завел. :-)))
Слишком много нужно учитывать для такой простой задачи.
На месте kulla в небольшой програмке и для себя я бы наверное тоже такой же код использовал. Но, по моему мнению, делать такие вещи для заказчиков (серьезных) - это просто плохой стиль.


kull   (2002-06-20 17:25:00) [6]

2Kaban

И еще насчет разницы:

type
TClass1 = class
end;

TClass2 = class
end;

var
a: TClass1;
b: TClass2;
begin
a := TClass1(b);//компилится
a := b as TClass1;//не компилится (incompatible types)
...


Похоже разница все же есть.


eheyler   (2002-06-20 17:02:00) [7]

2Kaban © (20.06.02 16:22)
Ошибки бывают разные.

Первая - AccessViolation.
(Если повезёт - ошибки может не быть, только объект попортишь).

А вторая - InvalidCast, которая ничего не портит и может быть
корректно обработана.

Разница в том, что во втором случае поведение программы прогнозируемо, а в первом - нет.


kull   (2002-06-20 16:48:00) [8]

А разница в том, например, что при использовании интерфейсов оператор as прибавляет счетчик ссылок на объект.

И к тому же наверное есть разница между просто приведением типа и динамическим с помощью as?

и еще раз повторюсь: Timer1 is TWinControl дает False...


Kaban   (2002-06-20 16:22:00) [9]

Sender_2:=Edit1;
TEdit(Sender_2).Text:="111"; //Работает нормально

Sender_2:=Edit1;
(Sender_2 as TEdit).Text:="111"; //Работает нормально

Sender_2:=Timer1;
TEdit(Sender_2).Text:="111"; //Выдает ошибку

Sender_2:=Timer1;
(Sender_2 as TEdit).Text:="111"; //Выдает ошибку

Ну и в чем разница?


eheyler   (2002-06-20 16:11:00) [10]

2Kaban © (20.06.02 15:42)
>Потому, что
>TEdit(AWinControl)
>(AWinControl as TEdit)
>эквивалентны

Это то есть как ???

Первым способом можно можно к TEdit всё что угодно, хоть число 2002.

А во втором случае будет сделана проверка настоящего типа AWinControl, и если это не потомок TEdit, будет эксепшн.
(поэтому в приведённом nick_sniper примере нужно именно
TEdit(AWinControl) - но, как он правильно заметил ниже, это изврат).


Обращение же к защищённым членам с помощью
TMyWinControl = class(TWinControl)
end;
- это стандартная вещь.


Kaban   (2002-06-20 16:03:00) [11]

Вот именно


Игорь Шевченко   (2002-06-20 15:52:00) [12]

Kaban © (20.06.02 15:42)

Тем более, что в описании функции тип параметра указан, как TWinControl. Передача TTimer вызовет ошибку компиляции


Kaban   (2002-06-20 15:42:00) [13]

Потому, что
TEdit(AWinControl)
(AWinControl as TEdit)
эквивалентны


kull   (2002-06-20 15:37:00) [14]


> (AWinControl as TEdit).OnClick := MyClick;
> AWinControl is TWinControl
> as is
> Чувствуешь разницу?


Не понял причем здесь разница.
Совершенно разные несравниваемые вещи asи is

Я вроде спрашивал почему надо динамически приводить типы с помощью as, а не просто TEdit(AWinControl).


А is - это я к тому что TTimer - это не TWinControl и оператор is это прекрасно проверяет.

Что-то я не понял о чем ты...


Kaban   (2002-06-20 15:18:00) [15]

(AWinControl as TEdit).OnClick := MyClick;
AWinControl is TWinControl
as is
Чувствуешь разницу?


kull   (2002-06-20 11:01:00) [16]


> (AWinControl as TEdit).OnClick := MyClick;

А почему не так: TEdit(AWinControl)?


> И вот здесь то и кроется огромное западло: TTimer не наследник
> TWinControl. А процедура может сработать и не выдать ошибку
> ! Есть такой шанс.


Что это за шанс такой?
Если TTimer не TWinControl то, я так думаю,
AWinControl is TWinControl будет False.

А насчет переопределения чужих обработчиков, то я собираюсь это использовать в своем компоненте (предке) и именно для того чтобы кол-во строчек было 10 а не 110


Kaban   (2002-06-20 09:43:00) [17]

Согласен с Игорем Шевченко. Когда мне нужно было достучаться до свойства InplaceEditor в TStringGrid, я делал именно так.


Игорь Шевченко   (2002-06-20 09:36:00) [18]

Такое приведение типов абсолютно нормально.


nick_sniper   (2002-06-20 04:38:00) [19]

Зачем создавать своего наследника? Можно доступаться намного проще:

procedure TForm1.SetHandlers(AWinControl: TWinControl);
begin
//можно ли так доступаться к protected свойству?
(AWinControl as TEdit).OnClick := MyClick;
end;


Не важно какой класс ставить TEdit или что-то другое, лишь бы он был наследником TWinControl и имел published OnClick;
В результате вы получите корректный доступ к этому полю по причине наследуемости объектов в Делфи.

НО !!!

1. Это прекрасно работает если у Вас 10 строчек кода. Такой стиль программирования не подходит при работе над большими проектами, т.к. ошибку такого класса можно вылавливать неделями.
2. Плохим стилем является программно присваивать обработчикам свои функции. Через три дня (или три месяца) Вы в ObjectInspector`е создаете Onclick обработчик а он почему-то не выполняется. И попробуйте вспомнить что Вы заменяете при вызове процедуры SetHandlers. Такую ошибку Вы долго будете искать...
3. Пример обработки, который часто встречается. Я вызываю Вашу процедуру:

for i := 0 to ComponentCount-1 do
if (Components[i] is TRxSplitter) or (Components[i] is TEdit) or (Components[i] is TTimer) then
Form1.SetHandlers(Components[i] as TWinControl);


И вот здесь то и кроется огромное западло: TTimer не наследник TWinControl. А процедура может сработать и не выдать ошибку ! Есть такой шанс.
4. Это не приемлемый метод программирования при коллективной работе.
5. Это будет корректно работать по Дельфи № 6. Вы можете гарантировать что это правильно будет работать и в седьмых Дельфях ? То-то же.

ВЫВОД: если очень хочется - то можно. НО ЛУЧШЕ НЕ НАДО. Всегда есть множество других методов решения проблемы.


kull   (2002-06-20 01:48:00) [20]


> З.Ы. OnClick - Это же не protected свойство (впрочем, неважно)


Заглянем в исходники класса TWinControl и обнаруживаем, что OnClick - protected свойство (именно в TWinControl, а не TEdit, например).


> Если вы планируете передавать в процедуру контролы различных
> типов, то проверяйте, являются ли они теми контролами, которые
> вы знаете (напр. стандартные, типа TEdit, TButton и др.).


В процедуре я запросто могу проверить:
if AWinControl is TWinControl then ...

ведь TEdit, TButton - это наследники TWinControl.

А причем здесь Access Violation? Контрол передается не nil (это тоже проверить можно), а свойство OnClick всяко у него есть т.к. оно наследуемое...


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо