如何在Delphi中覆盖继承的类属性?

时间:2013-11-20 16:03:05

标签: delphi oop properties

在一个文件中,我有一个带有ID属性的基类:

type
  TBase = class
  private  
    class function GetID(ACombo: TCombo): Integer; virtual;
    class procedure SetID(ACombo: TCombo; AValue: Integer); virtual;
  public  
    class property ID[ACombo: TCombo]: Integer read GetID write SetID;  
  end;

在第二个文件中,我有另一个类,从TBase下降。无论是偶然的,还是无知的,都是一个与现有财产/领域同名的新财产/领域。

type
  TSubBase = class(TBase)
  private  
    class function GetID(ACombo: TCombo): Integer; override;
    class procedure SetID(ACombo: TCombo; AValue: Integer); override;
  end;

并以下一种方式使用这些类:

  TBaseClass = class of TBase;

  function Base(): TBaseClass;

implementation

var
  BaseInstance: TBaseClass;

function Base(): TBaseClass;
begin
  if not Assigned(BaseInstance) then
  begin
     if SOME_PARAM then
      BaseInstance:= TBase
    else
      BaseInstance:= TSubBase;
  end;
  Result := BaseInstance;
end;

if Base.StationCode[cmbStation] = SOME_VALUE then

但是我在编译时遇到了错误:

[DCC Error] uMyFile.pas(69): E2355 Class property accessor must be a class field or class static method


我一直在尝试使用静态关键字......并根据以下同事的建议找到了一些解决方法。

type
  TBase = class
  private  
    class function GetIDStatic(ACombo: TCombo): Integer; static;
    class procedure SetIDStatic(ACombo: TCombo; AValue: Integer); static;    
    class function GetID(ACombo: TCombo): Integer; virtual; abstract;
    class procedure SetID(ACombo: TCombo; AValue: Integer); virtual; abstract;
  public  
    class property ID[ACombo: TCombo]: Integer read GetIDStatic write SetIDStatic;  
  end;

  TSubBase = class(TBase)
  private  
    class function GetID(ACombo: TCombo): Integer; override;
    class procedure SetID(ACombo: TCombo; AValue: Integer); override;
  end;

  TBaseClass = class of TBase;

  function Base(): TBaseClass;

implementation

var
  BaseInstance: TBaseClass;

function Base(): TBaseClass;
begin
  if not Assigned(BaseInstance) then
  begin
     if SOME_PARAM then
      BaseInstance:= TBase
    else
      BaseInstance:= TSubBase;
  end;
  Result := BaseInstance;
end;

class function TBase.GetIDStatic(ACombo: TCombo): Integer; static;
begin
  Result := BaseInstance.GetID(ACombo);
  // Or maybe below ?
  // Result := Base().GetID(ACombo); 
end;

class procedure TBase.SetIDStatic(ACombo: TCombo; AValue: Integer); static;
begin
  BaseInstance.SetID(ACombo, AValue);
  // Or maybe below ?
  // Base().SetID(ACombo, AValue); 
end;

但是在最后一个变体中 - 实现是丑陋的,我同意大卫关于使用类属性和SIMPLY重构的方法留下“梦想”,如下所述:

  class properties ID[ACombo: TCombo]: Integer ....
  =>> 
  class function GetID(ACombo: TCombo): Integer; virtual;
  class pocedure SetID(ACombo: TCombo; AValue: Integer); virtual; 

感谢大家在那里挖掘乐趣!

2 个答案:

答案 0 :(得分:3)

错误本身告诉:“类属性访问器必须是...... class static 方法”

你告诉过你可以编译TBase,但是当你添加TSubBase时会弹出错误。 但是不应该允许编译TBase。如果是 - 那么Delphi中就有一个错误。

http://docwiki.embarcadero.com/RADStudio/XE5/en/Methods#Class_Methods

  

在类方法的定义声明中,标识符为Self   表示调用方法的类(可以是   定义它的类的后代。)如果方法是   在C类中调用,然后Self属于C的类型类。因此你   无法使用Self访问实例字段,实例属性和   正常(对象)方法。您可以使用Self来调用构造函数和   其他类方法,或访问类属性和类字段。

所以我们可以想到一些解决方法,明确说明我们想要调用方法的类。这样的事情:

class function GetIDStatic(ACombo: TCombo): Integer; static;
var RealClass: TBaseClass;
begin
  RealClass := Self; /// will not compile !!!
  Result := RealClass.GetID(ACombo);
end;

但是...

  

与普通的类方法不同,类静态方法根本没有Self参数

因此静态方法无法知道在调用站点调用了哪个类。因此,它们恰好称为函数体,在自己的类中定义。在这个地方 - 这将是一个抽象的功能。

然而,有一个明显的解决方法可以编写像

这样的东西
if Base().n.StationCode[cmbStation] = SOME_VALUE then

然而,实施几乎没有效率,所以几乎不值得。

答案 1 :(得分:2)

编译器说:

  

类属性访问器必须是类字段或类静态方法

这意味着你运气不好。虽然可以实现virtual class methods,但它们不能用作类属性访问器。

documentation确实明确地说明了这一点:

  

可以在没有对象引用的情况下访问类属性。类属性访问器本身必须声明为类静态方法或类字段

由此我们得出结论,类属性在编译时绑定到它们的访问器,并且根本没有可以产生运行时动态绑定的技巧。如果你想要多态行为,你将不得不使用类方法。