Delphi - наследовать / переопределять постоянный массив? - PullRequest
2 голосов
/ 21 июля 2009

Во-первых, извинения за длину этого поста. Если краткость - это душа остроумия, то это глупый вопрос.

Я думаю, что мой вопрос сводится к:

Каков наилучший способ переопределить константный массив в дочерних классах Delphi?

Справочная информация:

= - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = -

У меня есть константный массив, определенный в родительском классе, а также во многих дочерних классах. Тип элементов массива всегда один и тот же, но количество элементов и точные данные отличаются от одного потомка к другому (я описываю таблицы базы данных из-за определенного элемента управления сеткой, который требует эти метаданные во время компиляции, но это не относится к делу).

У меня есть несколько функций, которые действуют на эти массивы. В качестве тривиального примера у меня может быть функция для возврата последнего элемента массива.

Если вы определите «GetLastElement» в родительском элементе, а затем вызовите эту унаследованную функцию от дочернего элемента, она все равно будет действовать на родительскую версию массива. Это не то, что я ожидал. Похоже, что дети должны вызывать унаследованную функцию в своей локальной версии массива.

В настоящее время я должен дублировать эти функции в каждом дочернем классе, что сводит с ума.

Я хочу, чтобы у меня была унаследованная функция , действующая на локальной версии моего константного массива. Каков наилучший способ сделать это? Я думал об определении функции в базовом классе, которая возвращает статический массив, а затем о переопределении этого для каждого дочернего элемента. Если бы я это сделал, я бы не работал с массивом, я бы работал с результатом функции.

Это решило бы проблему наследования, но оно вводит новую проблему в том, что Мне нужно будет определить новый тип для инкапсуляции массива и изменить мой (уже свернутый) элемент управления сеткой для работы этот тип.

Любые предложения приветствуются.

Ниже приведено упрощенное приложение, демонстрирующее, о чем я говорю:

В основной форме:

implementation

{$R *.dfm}

uses
  ParentClass, Descendant1, Descendant2;

procedure TfrmMain.btnTestClick(Sender: TObject);
var
  d1, d2: TParentClass;
begin
  d1 := TDescendant1.Create;
  d2 := TDescendant2.Create;

  //as it stands, this will return "E", then "A", which is good.
  //but if you move the LastElementOfArray function to the ParentClass,
  //then it will return "E", "E", ignoring the version of the array
  //defined inside TDescendant2.
  ShowMessage('d1=' + d1.LastElementOfArray);
  ShowMessage('d2=' + d2.LastElementOfArray);
end;


end.

В файле с именем ParentClass.pas:

unit ParentClass;

interface

type
  TParentClass = class
  public
    function LastElementOfArray: string; virtual; abstract;
  end;

const
  c_MyConstantArray: array[1..5] of string = ('A','B','C','D','E');

implementation

end.

В подразделении под названием Descendant1.pas

//here, we will just take whatever array we got from the parent
unit Descendant1;

interface

uses
  ParentClass;

type
  TDescendant1 = class(TParentClass)
  public
    function LastElementOfArray: string; override;
  end;

implementation

{ TDescendant1 }

function TDescendant1.LastElementOfArray: string;
begin
  Result := c_MyConstantArray[High(c_MyConstantArray)];
end;

end.

В файле с именем Descendant2.pas

//override with a new version of the constant array (same length)
unit Descendant2;

interface

uses
  ParentClass;

type
  TDescendant2 = class(TParentClass)
  public
    function LastElementOfArray: string; override;
  end;

const
  c_MyConstantArray: array[1..5] of string = ('E','D','C','B','A');

implementation

{ TDescendant2 }

function TDescendant2.LastElementOfArray: string;
begin
  //I hate defining this locally, but if I move it to ParentClass,
  //then it will act on the ParentClass version of the array, which
  //is **NOT** what I want
  Result := c_MyConstantArray[High(c_MyConstantArray)];
end;

end.

Ответы [ 2 ]

9 голосов
/ 21 июля 2009

Вместо этого вы можете рассмотреть возможность работы с динамическими массивами. Динамический массив может быть инициализирован в выражении, подобном этому:

type
  TStringArray = array of string;
// ...
var
  x: TStringArray;
begin
  x := TStringArray.Create('A', 'B', 'C')
end;

При таком подходе вы можете поместить определение массива в, например, средство получения свойства виртуального класса или переменная класса с ленивой инициализацией (посредством виртуального вызова), доступ к которой осуществляется через средство получения свойства класса. Затем методы, которые должны работать с определением массива «этим» классом, могут просто использовать виртуальный геттер через свойство.

Использование именованного динамического массива также избавляет от необходимости иметь отдельные типы для массивов различной длины, не теряя возможности инициализации в выражении.

1 голос
/ 21 июля 2009

Самый простой способ справиться с этим - иметь защищенное свойство вашего массива (динамическое, как предложил Барри) и назначить это свойство (при необходимости) в своем конструкторе. Затем ваш родитель может реализовать этот внутренний массив, и все ваши дети наследуют эту функциональность.

Проблема вашего подхода путем прямой ссылки на константы - это одна из задач. Каждый раз, когда делается ссылка, она будет идти против того, что она видит, сначала поднимаясь по разделу локальной реализации (от вызова вверх), затем к интерфейсу, затем к единицам в реализации, а затем к единицам в интерфейсе , При использовании ссылки больше не имеет значения, что находится в области видимости или вне области видимости ... ваш объект всегда может использовать ссылку для работы с тем, с чем он в настоящее время предназначен для работы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...