freepascal Конфликт имен между именем аргумента метода и именем публичного поля класса - Error: Duplicate identifier
Primary tabs
Рассмотрим пример кода:
type Cat = class public name: string; constructor create(name: string); procedure sayHello(); end; constructor Cat.create(name: string); begin self.name := name; end; procedure Cat.sayHello(); begin writeln('Привет, я ' + self.name + '!'); end; var CatItem: Cat; begin CatItem := Cat.create('Мурка'); CatItem.sayHello(); end.
- для такого кода fpc выбросит ошибку:
Compile Project, Target: .. Exit code 1, Errors: 1, Hints: 1
project1.lpr(5,24) Error: Duplicate identifier "name"
-- компилятор сообщает, что "идентификатор дублируется", т.е. что имя name уже используется
Что происходит - пояснение
Описать происходящее можно следующим образом:
для полей уровня видимости public компилятор не дает использовать те же имена, что и у полей класса
Это не типичная проблема - в большинстве современных языков вы не обнаружите сложностей такого рода, т.к. имя аргумента функции "видно" только внутри этой функции/класса и оно перекрывает все остальные значения с те же имененем (даже если они как-то доступны из тела функции/метода) без возникновения конфликта
Тем не менее, если вы хотите глубже разобраться с возникней проблемой именно в Паскале (на примере fpc freepascal), то давайте рассмотрим возможные решения проблемы и сделаем вывод
Возможное решение №1 - просто переключить режим компилятора
Этот вариант просто снимает проблему без какой-либо модификации кода, переключаем режим компилятора в вариант delphi:
{$mode delphi} // как в делфи ;) type Cat = class public name: string; constructor create(name: string); procedure sayHello(); end; constructor Cat.create(name: string); begin self.name := name; end; procedure Cat.sayHello(); begin writeln('Привет, я ' + self.name + '!'); end; var CatItem: Cat; begin CatItem := Cat.create('Мурка'); CatItem.sayHello(); end.
-- так как тут проблема просто снята, то все примеры ниже будут в режиме по умолчанию и там мы будем обходить это ограничение, модифицируя код.
Возможное решение №2 - изменение имени аргумента метода/поля класса
Изменим имя аргумента нашего конструктора (конструктор - тоже метод):
type Cat = class public name: string; constructor create(nameValue: string); procedure sayHello(); end; constructor Cat.create(nameValue: string); begin self.name := nameValue; end; procedure Cat.sayHello(); begin writeln('Привет, я ' + self.name + '!'); end; var CatItem: Cat; begin CatItem := Cat.create('Мурка'); CatItem.sayHello(); end.
-- такой код заработает, смысл
Возможное решение №3 - Использовать непубличную (отличную от public) область видимости
ПРИМЕЧАНИЕ: для понимания этого способа следует почитать про модификаторы доступа/области видимости элементов класса.
Если использовать, например модификатор protected для одного из конфликтующих элементов, скажем мы можем отправить в область защищенных (protected) элементов поле класса:
type Cat = class public constructor create(name: string); procedure sayHello(); protected name: string; // закрытое поле end; constructor Cat.create(name: string); begin self.name := name; end; procedure Cat.sayHello(); begin writeln('Привет, я ' + self.name + '!'); end; var CatItem: Cat; begin CatItem := Cat.create('Мурка'); CatItem.sayHello(); end.
-- такой код заработает, тут в более закрытую область () мы убрали поле класса.
а что есть убрать конфликтующий метод в закрытую область? Рассмотрим это ниже
Проверка: поможет ли оставление поля в публичной области и перемещении метода в более закрытую (гипотеза внутри "Возможного решения 3 ")
Обычно во многих языках программиромания сделать конструктор непубличным не получится, в freepascal такая возможность есть (за счет того, что комплятор по сути просто игнорирует protected для конструктора).
Чтобы показать, что речь в целом не только о конструкрах но и о всех методах, перепишем установку значения поля в конструкторе на обычную процедуру, а конструктор будем использовать тот, что есть у любого класса (стандартный, без аргументов, доступный по умолчанию).
Мы не можем напрямую снаружи вызвать защищенный (protected) метод setRealName() (именно с ним будем моделировать конфликт имени), поэтому обернем вызов публичным setName(), который использует отличное от имени поля имя своего аргумента (см. возможное решение №2 выше)
type Cat = class public name: string; procedure sayHello(); procedure setName(nameValue: string); protected procedure setRealName(name: string); end; procedure Cat.sayHello(); begin writeln('Привет, я ' + self.name + '!'); end; procedure Cat.setName(nameValue: string); begin self.setRealName(nameValue); end; procedure Cat.setRealName(name: string); begin self.name := name; end; var CatItem: Cat; begin CatItem := Cat.create(); // пустой конструктор по умолчанию CatItem.setName('Мурка'); // установим имя CatItem.sayHello(); end.
-- запуск такого код опять приведет в выбросу ошибки:
project1.lpr(8,27) Error: Duplicate identifier "name"
ВЫВОД: это значит, что для решения проблемы защищенным (или еще более закрытым - тут надо проверять) должно быть именно поле класса, а не поле, публичное поле продолжает конфликтовать, даже если методы находятся в более закрытых областях, напр. в защищенной.
Материалы по теме
- Duplicate identifier of property and method parameter of a class: https://stackoverflow.com/questions/4415...
- ООП Паскале. Введение: Класс, объект, конструктор, метод, поле
- Модификаторы доступа элементов класса: public, private, protected. Инкапсуляция
Этот разбор посвящается участнику @drl - отважному исследователю программирования, [будущему, частично уже] знатоку английского языка и автору наводящего вопроса ;)
- Log in to post comments
- 273 reads