首先,对这篇文章的篇幅表示道歉。如果简洁是智慧的灵魂,那么这是一个无知的问题。
我认为我的问题归结为:
在Delphi子类中覆盖常量数组的最佳方法是什么?
背景:
= - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = -
我有一个在父类中定义的常量数组,也有很多子类。 数组元素的类型总是相同的,但是元素的数量和确切的数据从一个子节点到另一个子节点不同(我正在描述数据库表,因为在编译时需要元数据的特定网格控件,但那不是重点。)
我有几个函数可以作用于这些数组。作为一个简单的例子,我可能有一个函数来返回数组的最后一个元素。
如果在父级中定义“GetLastElement”,然后从子级调用该继承函数,它仍将作用于该阵列的父级版本。这不是我的预期。看起来孩子们应该在他们自己的本地版本的数组上调用继承的函数。
目前我必须在每个子类中复制这些函数,这很令人抓狂。
我希望能够在我的常量数组的本地版本上使用继承函数。最好的方法是什么?我已经考虑过在基类中定义一个返回静态数组的函数,然后为每个子项覆盖它。如果我这样做,那么我就不会在数组上进行操作,我会根据函数结果进行操作。
这将解决继承问题,但它在I'd have to define a new type to encapsulate the array中引入了一个新问题,并修改了我的(已经复杂的)网格控件以使用该类型。
欢迎任何建议。
下面是一个简化的应用程序,演示了我在说什么:
以主要形式:
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.
答案 0 :(得分:9)
您可以考虑使用动态数组。动态数组可以在这样的表达式中初始化:
type
TStringArray = array of string;
// ...
var
x: TStringArray;
begin
x := TStringArray.Create('A', 'B', 'C')
end;
使用这种方法,您可以将数组定义放在例如虚拟类属性getter,或通过类属性getter访问的延迟初始化(通过虚拟调用)类变量。然后,需要使用“this”类的数组定义的方法可以通过属性简单地使用虚拟getter。
使用命名的动态数组也远离了必须为不同长度的数组设置单独类型的问题,而不会失去在表达式中初始化的能力。
答案 1 :(得分:1)
处理此问题的最简单方法是拥有数组的受保护属性(动态为Barry建议),并在构造函数中指定此属性(如果需要)。然后,您的父级可以针对此内部数组实施,您的孩子将继承该功能。
直接引用常量的方法问题是范围之一。每次引用时,它将与它看到的相反,首先是本地实现部分(从调用向上),然后是接口,接着是实现中的单元,接着是接口中的单元。通过使用引用,不再重视范围内或范围内的内容......您的对象始终可以使用引用来处理当前针对其操作的内容。