Как мы видели ранее, возможно получить доступ для записи для свойства только для чтения, вернее, к его полю данных. Также вы можете повысить видимость protected-свойств до public или published, чтобы получить к ним доступ, но у нас нет соответствующего синтаксиса, чтобы сделать это для полей или методов. Часто вам бывает просто необходимо вызвать protected-метод какого-то класса - часто это метод в VCL-компоненте или сторонней библиотеке кода или компонентов. Часто это является признаком плохого (недостаточно гибкого) дизайна класса. Автор кода не подумал, что коду уровня приложения может потребоваться доступ к этому методу. Т.е. грубо говоря, у нас есть такой код:
unit Utility; interface type TUtility = class protected procedure Useful; public constructor Create; procedure Boring; end;Мы хотим вызвать protected-метод Useful, но всё, к чему мы имеем доступ, - это public-метод Boring. Типичная ситуация, да? ;)
Если вам повезёт, то вы имеете контроль над типом используемых экземпляров, так что вы можете написать своего наследника, который делает метод доступным "чистым способом" (т.е. документировано, не хаком), вроде такого:
type TMyUtility = class(TUtility) public procedure Useful; end; procedure TMyUtility.Useful; begin inherited Useful; end;Теперь вы просто можете использовать TMyUtility всюду вместо TUtility - и у вас будет доступен метод Useful. Последний штрих: использовать в точности то же имя класса, например:
unit MyUtility; interface uses Utility; type TUtility = class(Utility.TUtility) public procedure Useful; end;Теперь вам нужно только добавить ссылку на модуль MyUtility в список uses после ссылки на модуль Utility.
Однако, иногда у вас нет контроля над типом используемых экземпляров - к примеру, они создаются внутри стороннего кода.
Как знают многие продвинутые Delphi-программисты, вы легко можете получить доступ к protected-членам класса, просто приводя экземпляр к классу-наследнику, объявленному в том же модуле, что и выражение с приведением типов. Например, чтобы вызвать protected-метод Useful экземпляра класса TUtility, вы можете просто написать:
type TUtilityEx = class(TUtility) end; procedure Foo(Instance: TUtility); begin // Не компилируется: у вас нет доступа к protected-методу Instance.Useful; // Компилируется отлично, вызывает protected-метод TUtilityEx (Instance).Useful; end;Способ работы директив видимости Delphi позволяет всему коду, объявленному в том же модуле, иметь полный доступ ко всем членам класса. Это вроде неявной концепции дружественных классов из C++.
Один очень обещающий поставщик компонентов обычно прячет даже действительно полезные (а иногда и необходимые) методы своих компонентов и вспомогательные классы в protected раздел - скорее всего, только в попытке скрыть их сложность от наивных пользователей. Затем во всех своих модулях, которые должны иметь доступ к этим методам, они делают указанный выше хак с объявлением Dummy-класса для своих собственных классов. Ужасно! Этот хак должен использоваться только для получения доступа к чужому коду, а не использоваться для взаимодействия между своими модулями!
Любопытно, что компилятор использует этот локальный хак-класс только для получения доступа к protected-методу. Хотя класс TUtilityEx упоминается в исходном коде, но ничего из этого класса (VMT, RTTI и т.п.) не нужно для скомпилированного кода. Поэтому умный линкёр удаляет лишнюю информацию из исполняемого модуля. В противном случае, этот хак был бы весьма накладен с точки зрения размера кода.
Читать далее.
Где номер 3? ;-)
ОтветитьУдалитьЯ перевожу только интересные (для меня) посты. Хак №3 - это включение JIT-оптимизатора для D8 .NET. Мне это не интересно.
ОтветитьУдалитьatrus-at-lj вы тоже вспомнили историю про свиней в американской школе?
ОтветитьУдалитьГы, а я не слышал раньше :))
ОтветитьУдалитьЭх, вот еслиб можно было, без извращений, к приватным полям достучаться... Вроде TWinControl.FControls
ОтветитьУдалитьЭто достаточно старая фишка. Очень давно читал тоже самое в рунете...
ОтветитьУдалить>> Эх, вот еслиб можно было, без извращений, к приватным полям достучаться...
И слава Богу, что нельзя! Ведь это - основа Объектно-ориентированной концепции, в которой класс рассматривается как ящик пандоры. То есть то, что используется внутри не уместно для внешнего использования (закрытый ящик), а пытаешься открыть и тут те БАЦ! Куча проблем и надежда вида: - "Ну заработай! Пожалуйста заработай!!!" .
>> И слава Богу, что нельзя!
ОтветитьУдалитьНу мне вот надо сделать простую вещь - узнать, лежат ли на TWinControl, наследники от TControl (если нет, то можно очень сильно ускорить отрисовку). Еслиб FControls было хотя бы в protected, то решение простое: (FControls <> nil), а теперь приходится перебирать все контролы (ControlCount, Controls[]) и тестировать каждый оператором is, что не выглядит быстрым и изящным. И теперь вопрос - сильно ли меня волновали принципы OOП, когда я увидел, что FControls в private? :)
Я не про то, что совсем уж нельзя, просто это не нужно ) Если разработчики скрыли поле - значит они понимают, что Вам оно не нужно. Там ведь тоже не дураки сидят, так что думаю, если оно было бы нужно как открытое - открыли бы. Получить доступ к приват полям можно, о чём свидетельствует тот же 5 хак от Хальварда (можно найти на его блоге, ссылка вверху).
ОтветитьУдалитьИли тут.
ОтветитьУдалить> Один очень обещающий поставщик компонентов обычно прячет даже действительно полезные (а иногда и необходимые) методы своих компонентов и вспомогательные классы в protected раздел
ОтветитьУдалитьМногие поставщики компонентов (например TMS Scripter Pro) прячут действительно полезный код в секции private, что делает практически невозможным кастомизацию их классов через написание своих наследников. И подозреваю, что это делается специально.