代码之家  ›  专栏  ›  技术社区  ›  JosephStyons

Delphi-继承/重写常量数组?

  •  2
  • JosephStyons  · 技术社区  · 15 年前

    首先,为这篇文章的长度道歉。如果简洁是智慧的灵魂,那么这是一个无智的问题。

    我想我的问题可以归结为:

    在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.
    
    2 回复  |  直到 15 年前
        1
  •  9
  •   Barry Kelly    15 年前

    您可以考虑改用动态数组。可以在如下表达式中初始化动态数组:

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

    使用这种方法,您可以将数组定义放在例如虚拟类属性getter或通过类属性getter访问的延迟初始化(通过虚拟调用)类变量中。然后,需要处理“this”类的数组定义的方法可以通过属性简单地使用虚拟getter。

    使用命名的动态数组还可以避免为不同长度的数组提供不同类型的问题,而不会失去在表达式中初始化的能力。

        2
  •  1
  •   skamradt    15 年前

    处理这一问题的最简单方法是拥有数组的受保护属性(如巴里建议的那样是动态的),并在构造函数中分配此属性(如有必要)。然后,您的父级可以针对这个内部数组实现,您的子级将全部继承该功能。

    直接引用常量的方法的问题是范围问题之一。每次引用时,它都会与所看到的相反,首先是本地实现部分(从调用开始),然后是接口,然后是实现中的单元,最后是接口中的单元。通过引用,什么在范围内或不在范围内不再重要…您的对象总是可以使用引用来处理它当前设计用来操作的对象。