freepascal Конфликт имен между именем аргумента метода и именем публичного поля класса - Error: Duplicate identifier

Рассмотрим пример кода:

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"

ВЫВОД: это значит, что для решения проблемы защищенным (или еще более закрытым - тут надо проверять) должно быть именно поле класса, а не поле, публичное поле продолжает конфликтовать, даже если методы находятся в более закрытых областях, напр. в защищенной.

Материалы по теме

Этот разбор посвящается участнику @drl - отважному исследователю программирования, [будущему, частично уже] знатоку английского языка и автору наводящего вопроса ;)