Форум: "Основная";
Текущий архив: 2002.07.04;
Скачать: [xml.tar.bz2];
ВнизКорректно ли такое приведение типов? Найти похожие ветки
← →
kull (2002-06-20 00:27) [0]Привет народ!
Наткнулся на следующую проблемму:
В мою процедуру передается некий контрол,о котором я знаю только что он - 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;
Проект компиляется и работает, но правильно ли так делать и не грозит ли это глюками в дальнейшем?
← →
Cobalt (2002-06-20 01:28) [1]>В мою процедуру передается некий контрол,о котором я знаю только что он - TWinControl.
В свете вышесказанного - приведение типов абсолютно некорректно, делать так - неправильно, грозит глюками и Access Violation.
Если вы планируете передавать в процедуру контролы различных типов, то проверяйте, являются ли они теми контролами, которые вы знаете (напр. стандартные, типа TEdit, TButton и др.).
З.Ы. OnClick - Это же не protected свойство (впрочем, неважно)
← →
kull (2002-06-20 01:48) [2]
> З.Ы. OnClick - Это же не protected свойство (впрочем, неважно)
Заглянем в исходники класса TWinControl и обнаруживаем, что OnClick - protected свойство (именно в TWinControl, а не TEdit, например).
> Если вы планируете передавать в процедуру контролы различных
> типов, то проверяйте, являются ли они теми контролами, которые
> вы знаете (напр. стандартные, типа TEdit, TButton и др.).
В процедуре я запросто могу проверить:
if AWinControl is TWinControl then ...
ведь TEdit, TButton - это наследники TWinControl.
А причем здесь Access Violation? Контрол передается не nil (это тоже проверить можно), а свойство OnClick всяко у него есть т.к. оно наследуемое...
← →
nick_sniper (2002-06-20 04:38) [3]Зачем создавать своего наследника? Можно доступаться намного проще:
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. Вы можете гарантировать что это правильно будет работать и в седьмых Дельфях ? То-то же.
ВЫВОД: если очень хочется - то можно. НО ЛУЧШЕ НЕ НАДО. Всегда есть множество других методов решения проблемы.
← →
Игорь Шевченко (2002-06-20 09:36) [4]Такое приведение типов абсолютно нормально.
← →
Kaban (2002-06-20 09:43) [5]Согласен с Игорем Шевченко. Когда мне нужно было достучаться до свойства InplaceEditor в TStringGrid, я делал именно так.
← →
kull (2002-06-20 11:01) [6]
> (AWinControl as TEdit).OnClick := MyClick;
А почему не так: TEdit(AWinControl)?
> И вот здесь то и кроется огромное западло: TTimer не наследник
> TWinControl. А процедура может сработать и не выдать ошибку
> ! Есть такой шанс.
Что это за шанс такой?
Если TTimer не TWinControl то, я так думаю,
AWinControl is TWinControl будет False.
А насчет переопределения чужих обработчиков, то я собираюсь это использовать в своем компоненте (предке) и именно для того чтобы кол-во строчек было 10 а не 110
← →
Kaban (2002-06-20 15:18) [7](AWinControl as TEdit).OnClick := MyClick;
AWinControl is TWinControl
as is
Чувствуешь разницу?
← →
kull (2002-06-20 15:37) [8]
> (AWinControl as TEdit).OnClick := MyClick;
> AWinControl is TWinControl
> as is
> Чувствуешь разницу?
Не понял причем здесь разница.
Совершенно разные несравниваемые вещи as и is
Я вроде спрашивал почему надо динамически приводить типы с помощью as, а не просто TEdit(AWinControl).
А is - это я к тому что TTimer - это не TWinControl и оператор is это прекрасно проверяет.
Что-то я не понял о чем ты...
← →
Kaban (2002-06-20 15:42) [9]Потому, что
TEdit(AWinControl)
(AWinControl as TEdit)
эквивалентны
← →
Игорь Шевченко (2002-06-20 15:52) [10]Kaban © (20.06.02 15:42)
Тем более, что в описании функции тип параметра указан, как TWinControl. Передача TTimer вызовет ошибку компиляции
← →
Kaban (2002-06-20 16:03) [11]Вот именно
← →
eheyler (2002-06-20 16:11) [12]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:22) [13]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"; //Выдает ошибку
Ну и в чем разница?
← →
kull (2002-06-20 16:48) [14]А разница в том, например, что при использовании интерфейсов оператор as прибавляет счетчик ссылок на объект.
И к тому же наверное есть разница между просто приведением типа и динамическим с помощью as?
и еще раз повторюсь: Timer1 is TWinControl дает False...
← →
eheyler (2002-06-20 17:02) [15]2Kaban © (20.06.02 16:22)
Ошибки бывают разные.
Первая - AccessViolation.
(Если повезёт - ошибки может не быть, только объект попортишь).
А вторая - InvalidCast, которая ничего не портит и может быть
корректно обработана.
Разница в том, что во втором случае поведение программы прогнозируемо, а в первом - нет.
← →
kull (2002-06-20 17:25) [16]2Kaban
И еще насчет разницы:
type
TClass1 = class
end;
TClass2 = class
end;
var
a: TClass1;
b: TClass2;
begin
a := TClass1(b);//компилится
a := b as TClass1;//не компилится (incompatible types)
...
Похоже разница все же есть.
← →
nick_sniper (2002-06-20 18:39) [17]2kull
Разница определенно есть. Фактически ты описал два наследника TObject, т.е. аналогично будет:
type
TClass1 = class(TObject)
end;
TClass2 = class(TObject)
end;
Получается, что ты создал два разных класса с общим предком.
Я наверное перестарался с запугиванием. Приношу свои извинения. Но ведь не зря же Borland из версии в версию прячет все поля в WinControle. Может быть для некоторых компонентов абсолютно противопоказан обработчик Onclick ? И пользователь не должен иметь возможности его вводить? Такая ситуация может запросто встретиться. А ты его в своей процедуре случайно установишь? Видишь какую дискуссию создал и в какие дебри программирования ноарод завел. :-)))
Слишком много нужно учитывать для такой простой задачи.
На месте kulla в небольшой програмке и для себя я бы наверное тоже такой же код использовал. Но, по моему мнению, делать такие вещи для заказчиков (серьезных) - это просто плохой стиль.
← →
kull (2002-06-20 18:53) [18]
> nick_sniper © (20.06.02 18:39)
Да согласен.
Просто я пишу набор компонентов, которые являются составными, т.е. на TPanel лежат несколько контролов.
Надо поймать их событие OnClick и вывести наружу в общий обработчик для пользователя компонента.
Так вот для каждоко такого компонента надо писать типа:
AControl.OnClick := MyOnClick;
и еще по другим событиям OnDragOver, OnDragDrop и т.д.
Но если наследовать этот Panel и переопределить так, чтобы при создании на нем контрола автоматически прописывались обработчики, то было бы очень удобно.
← →
eheyler (2002-06-20 19:23) [19]Дело в том, что в паскале нельзя понизить видимость свойств,
и если св-во объявлено как published, то обратно его уже не
уберёшь. Поэтому в Дельфи принята практика Custom-конмпонентов, когда в Custom-предке все св-ва защищённые, а в потомках открываются те, которые нужны.
Из-за этого возникает вобщем-то неправильная с точки зрения ООП ситуация, когда несколько разных объектов наследуют св-во от одного предка, но к нему нельзя иметь доступ единообразно, приведя их к предку.
Эта ситуация обычно разрешается именно тем методом, который ты привёл, такой код встречается даже в исходниках VCL, и ничего зазорного в нём нет. Просто ты должен понимать, что делаешь.
← →
kull (2002-06-20 20:50) [20]Спасибо, Народ!
← →
Юрий Зотов (2002-06-21 02:09) [21]Пара замечаний к уже сказанному.
1. Такой способ получения доступа к protected-секции - это вполне законный прием и он действительно используется в самой VCL. Только слово "end" писать не нужно(хотя это и не ощибка) - вполне достаточно просто:
type
TMyWinControl = class(TWinControl);
2. А вот назначение обработчика
TMyWinControl(AWinControl).OnClick := MyClick;
я бы тоже назвал ОЧЕНЬ опасным приемом и ОЧЕНЬ опасным стилем. Таким способом не только затирается предыдущий обработчик, но и есть большой риск, что назначенный Вами тоже будет перезатерт. Как в итоге будет работать программа - непредсказуемо.
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2002.07.04;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.018 c