| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Delphi: Общие вопросы > Oшибка circular unit reference |
| Автор: Дмитрий01 29.8.2015, 17:20 | ||
Oшибка "circular unit reference" вот в таком коде:
Что делать? |
| Автор: Poseidon 29.8.2015, 17:37 |
| Пропиши во всех модулях остальные модули в uses после implementation Добавлено через 2 минуты и 26 секунд Хотя нет, у тебя тут все сложнее. Проще все в один модуль запихнуть. А вообще тут изначально ошиюбка в иерархии классов. |
| Автор: Дмитрий01 29.8.2015, 18:07 |
| В один модуль всё запихивать не хочу: слишком длинно будет. А что посоветуете на счёт иерархии? |
| Автор: Poseidon 29.8.2015, 19:21 |
| Не правильно использовать в базовом классе параметры, у которых тип - дочерний класс. Другими словами, родитель ничего не должен знать о своих потомках. |
| Автор: dnek 30.8.2015, 14:30 |
| Согласен с Poseidon. Я бы даже сказал что это недопустимо и ломает основополагающие принципы наследования. Но в данном случае проблема не в этом. Если Вы хотите использовать именно такую структуру unit-ов, то: 1. В BaseUnit TBase.owner объявить как TObject, в uses секции implementation добавить OwnerUnit а в коде использовать приведение к типу - TOwner(owner) 2. В OwnerUnit TOwner.a и TOwner.b объявить как TBase, в uses секции interface добавить BaseUnit а в коде использовать приведение к типу - TA(a) и TB(b) но лучше корректно спроектировать TBase и избежать этого Этот ответ добавлен с нового Винграда - http://ru.vingrad.com/Oshibka-circular-unit-reference-id55e1bfcfae20153e608b4567#findElement_E7045_55e2e93cae20159e491cc064_0 |
| Автор: Дмитрий01 30.8.2015, 21:53 | ||
Мне этого и не нужно. Мне нужно только, чтобы объект A мог при необходимости обратиться к B и наоборот. |
| Автор: Дмитрий01 1.9.2015, 13:08 |
| Я бы хотел, чтобы А и В обращались друг к другу, находясь на одном уровне иерархии. Разве это недопустимо? |
| Автор: Poseidon 1.9.2015, 13:52 | ||
|
| Автор: dnek 2.9.2015, 09:07 | ||||
Это верно только если А наследуется от В а не обращается.
Не только допустимо но еще и является стандартной часто используемой практикой. В Вашем случае есть 3 выхода из ситуации: 1. Классы обращающиеся друг к другу запихнуть в один юнит (самый простой, быстрый и удобный выход) 2. Использовать приведение к типу как я писал выше (наиболее часто используемый) 3. Ввести поддержку интерфейсов в этих классах и вынести их объявление в отдельный юнит (наиболее профессиональный подход но не всегда оправдывает затраченные время и силы) Этот ответ добавлен с нового Винграда - http://ru.vingrad.com/Oshibka-circular-unit-reference-id55e1bfcfae20153e608b4567#findElement_E7045_55e69205ae2015af251cc084_0 |
| Автор: Poseidon 2.9.2015, 11:50 |
dnek, в данном случае на лицо не верно спроектированная иерархия классов. TBase, на сколько я понимаю, задумывался как базовый класс, коим он и является для TA и TB. В то же время TBase содержит в себе параметр типа TOwner. И все бы хорошо, но TOwner содержит парамеры типа TA и TB. Другими словами, из TBase мы вполне себе можем достучаться до TA (TBase.owner.a). Т.е. из родителя имеем доступ к потомку, а это в корне не правильно. Пихание в один юнит избавит от ругательств компилятора, но не избавит от дальнейших ошибок на уровне проектирования. В лучшем случае нарвемся на вечный цикл и, как следствие, мертвое зависание программы. |
| Автор: dnek 2.9.2015, 14:23 |
| Да кто Вам сказал что это неправильно. Как пример - TControl.Parent и TComponent.Owner Другое дело, что TOwner.a и TOwner.b корректнее было бы объявить как TBase и правильно спроектировать его, но TOwner может не знать о TBase но знать TA и TB. Да и TBase лучше объявить как abstract. Этот ответ добавлен с нового Винграда - http://ru.vingrad.com/Oshibka-circular-unit-reference-id55e1bfcfae20153e608b4567#findElement_E7045_55e6dc4dae20155c1e1cbe87_0 |
| Автор: dnek 2.9.2015, 14:33 |
| Нельзя объявлять только что-то типа "TBase.A: TA;". Это действительно недопустимо. Только "TBase.A: TBase;" А с промежуточным классом, который не наследуется от TBase - сколько угодно и как угодно. Этот ответ добавлен с нового Винграда - http://ru.vingrad.com/Oshibka-circular-unit-reference-id55e1bfcfae20153e608b4567#findElement_E7045_55e6de91ae201508231cbdb5_0 |
| Автор: Poseidon 2.9.2015, 14:47 | ||
Возьмем хотя бы TControl. Базовый класс - TComponent. Возьмите реализацию TComponent, там нет ни намека на его потомка - TControl. Вы никак не выйдете из TComponent к TControl, кроме как прямым преобразованием. В нашем же случае с TBase можно легко выйти на потомка TA, а этого быть не должно. К TOwner вообще нет притензий, тут все норально. Вопросы есть к TBase. |
| Автор: dnek 2.9.2015, 15:00 | ||||
Теория и практика разные вещи Посмотрите на объявление TControl и TWinControl в модуле Controls:
После этого, если хотите, продолжим дискуссию :) Этот ответ добавлен с нового Винграда - http://ru.vingrad.com/Oshibka-circular-unit-reference-id55e1bfcfae20153e608b4567#findElement_E7045_55e6e4e7ae2015a6281cbdf7_0 |
| Автор: Poseidon 2.9.2015, 15:51 |
| Хорошо, дальше смотрели? Мы дальше где-нибудь в TControl используем функционал TWinControl? Его собственные методы? То, что там TWinControl, обослувленно Windows, которая требует, что бы родительским компонентом был только оконный компонент (что, в принципе, логично). Что бы это реализовать, пришлось огланичить все многообразие элементов управления Windows (TControl) только конкретно оконными компонентами (TWinControl). Грубо говоря, что бы в Parent нельзя было передать тот же TComponent или его наследников. Можем говорить о том, что это скорее исключение из правил и обусловлено это требованиями ОС. Приведу пример. Допустим в TBase есть какой-то метод, который создает owner.a (owner.a := TA.Create). Он через наследование доступен и в TA. В TA этот метод вызывается в конструкторе. Что произойдет? Зависон на ровном месте. |
| Автор: dnek 2.9.2015, 16:22 | ||
А это уже проблема не проектирования а реализации. Дедлоки можно сделать и на идеально спроектированных классах. Если програмер спроектировал иерархию классов то и реализовывать надо учитывая все тонкости проектирования. Тут уже все зависит от программиста и его профессионализма. Проектировать надо исходя из конкретной задачи и так, чтобы самому потом работать удобно было с этими классами. Практика показывает что для этого зачастую приходится закрывать глаза на многие аспекты теории. Этот ответ добавлен с нового Винграда - http://ru.vingrad.com/Oshibka-circular-unit-reference-id55e1bfcfae20153e608b4567#findElement_E7045_55e6f812ae201509371cc03a_0 |