【问题标题】:How to override an inherited class property in Delphi?如何在 Delphi 中覆盖继承的类属性?
【发布时间】:2013-12-04 17:37:06
【问题描述】:

在一个文件中,我有一个带有 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;

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

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

感谢大家在这里挖掘的乐趣!

【问题讨论】:

  • 是的,确实不需要重复。我修改了示例
  • 你还在苦苦挣扎吗?
  • 非常感谢大卫!是的,我找到了一些解决方法......但这很棘手,而且对我来说不包括方法
  • 你明白类属性是在编译时绑定的吗?从对问题的编辑和您的接受来看,您似乎还没有完全理解这一点。我想帮助你,但你似乎一心想要让类属性做他们不能做的事情。您最新编辑中的代码非常骇人听闻。并且可以使用虚拟类方法轻松解决。然后没有破解,代码看起来很干净。没有卑鄙的全局变量。你能告诉我你不明白我回答的哪一部分吗?
  • 大卫,我确实了解财产,但我不会弯腰。我只有一个限制,我无法重构生产代码。我自己不能接受这样的解决方案,无论如何都必须向我的上级询问重构这些类!

标签: delphi oop properties


【解决方案1】:

错误告诉自己:“类属性访问器必须是...类静态方法”

您告诉过您可以编译 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

但是实施起来几乎没有效率,所以不值得。

【讨论】:

  • 这是一个虚假的论点。您可以使用相同的参数来“证明”普通实例属性不能使用虚拟访问方法。
  • @DavidHeffernan 普通方法不适合没有实例使用,但类方法可以。
  • @MasonWheeler 再次针对类(class-ref var)调用构造函数,而不是针对实例调用构造函数。而 class-ref 正是指向 VMT 的指针。所以在上面那种情况下,SetIDStatic 应该在调用方法之前取Self 并确定它的真实类型......
  • @DavidHeffernan 坚持什么?我删除了您要求的内容,您没有刷新页面吗? “你没看到类属性是在编译时绑定的吗?”这有点重复错误消息本身。我做到了,然后你又做了一次,然后主题启动器阅读了它。那么,有什么地方应该让每个人都保持沉默吗?不能想一想解释发生了什么以及为什么?解决它?也许在来源上有一些细微的变化?
  • @DavidHeffernan 现在不那么沮丧了吗?
【解决方案2】:

编译器说:

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

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

documentation 确实明确指出了这一点:

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

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

【讨论】:

  • 再次更新示例。请看静态修改
  • 答案是一样的。类属性不能动态绑定。阅读我引用的文档链接以说服自己。
猜你喜欢
  • 2011-05-23
  • 2012-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-21
相关资源
最近更新 更多