【问题标题】:Is it possible to define a virtual static member on a delphi class?是否可以在 delphi 类上定义虚拟静态成员?
【发布时间】:2013-07-27 14:58:39
【问题描述】:

我需要创建类似的东西,我用 PHP 语言实现。

假设我创建了一个定义 2 个静态成员变量的基类,然后子类应该能够“覆盖”它们,所以如果我在 BaseClass 中定义了静态变量“someStatic”,然后我将子类化为 DerivedClass ,当我调用 TDerivedOne.someStatic 时,程序应该显示来自该派生类的 someStatic.. 但 Delphi 不是这种情况,我肯定是错误地实现了它..

目前,我实现了另一种设计方法,其中变量没有声明为静态,然后创建了一个名为'setup'的虚拟抽象方法,然后这个setup会在BaseClass构造函数上调用,但是这个设计需要在我们检索所需变量之前首先创建的对象。

现在出于好奇,我想知道是否可以实现虚拟静态变量来节省“几次打字……”

这里是问题的代码sn-p

program Project1;

{$APPTYPE CONSOLE}


uses
    SysUtils;


type
    TBaseClass = class
      protected
        class var someStatic: string;

      public
        class function get: string; virtual;
    end;

    TDerived = class( TBaseClass )
      (*
      //if i uncomment this section, then the print bellow will print the Base's static var
      protected
        class var someStatic: string;
      *)
    end;

    TDerived2 = class( TBaseClass )
    end;

class function TBaseClass.get: string;
begin
    Result := someStatic;
end;


begin
    // ------If the class is defined per unit, this one should be on the initialization section
    TBaseClass.someStatic := 'base';
    TDerived.someStatic := 'derived';
    TDerived2.someStatic := 'derived2';
    // ------END OF INITIALIZATION

    try
        //i'm expecting this would print 'derived' but it prints 'derived2' :'(
        //i am using DelphiXE
        //apparently delphi treat the statics differently from that of php's
        writeln( TDerived.get );
        readln;
    except
        on E: Exception do
            writeln( E.ClassName, ': ', E.Message );
    end;

end.

干杯:)

【问题讨论】:

  • 这里根本没有静态的东西。你有类方法和类变量。但没有什么是静态的。
  • 嗨@DavidHeffernan,感谢您指出这一点,我认为在方法前面加上“class”并在var前面加上“class var”将使它们“静态”与“class var”有什么区别"和真正的静态声明?派生的和父的static指向同一个地址吗?
  • Static 在 Delphi 中的含义与 C++ 不同。这里有类方法和类变量。静态类方法没有Self,静态实例方法不能被覆盖。
  • 我讨厌在不同编程语言之间使用不同的术语,尤其是在 Delphi 和 C++ 之间。其中之一就是那个类和静态的吧。
  • @BasePointer 我讨厌 C++ 中的静态,它在不同的上下文中重复使用,并且通常具有非常不同的含义!

标签: delphi oop virtual delphi-xe static-members


【解决方案1】:

由于所有继承中的 SomeStatic 都指向同一个位置,因此将保留最后的设置。
最接近您的要求的方法可能如下所示。

type
    TBaseClass = class
      protected
        class var someStatic: string;
      public
        class function get: string; virtual;
        class Procedure _Set(const Value:String);virtual;
    end;

    TDerived = class( TBaseClass )
      protected
        class var someStatic: string;
      public
        class function get: string; override;
        class Procedure _Set(const Value:String);override;
    end;

    TDerived2 = class( TBaseClass )
      protected
        class var someStatic: string;
      public
        class function get: string; override;
        class Procedure _Set(const Value:String);override;
    end;

class function TBaseClass.get: string;
begin
    Result := someStatic;
end;


{ TDerived }

class function TDerived.get: string;
begin
    inherited;
    Result := someStatic;
end;

class procedure TDerived._Set(const Value: String);
begin
    SomeStatic := Value;
end;

{ TDerived2 }

class function TDerived2.get: string;
begin
    inherited;
    Result := someStatic;
end;

class procedure TDerived2._Set(const Value: String);
begin
  SomeStatic := Value;
end;

class procedure TBaseClass._Set(const Value: String);
begin
  SomeStatic := Value;
end;

【讨论】:

    【解决方案2】:

    没问题,只要你也重写Get方法:

    program Project1;
    
    {$APPTYPE CONSOLE}
    
    type
      TBase = class
      protected
        class var FSomething: String;
        class function Get: String; virtual;
      end;
    
      TDerived1 = class(TBase)
      protected
        class var FSomething: String;
        class function Get: String; override;
      end;
    
      TDerived2 = class(TBase);
    
    class function TBase.Get: String;
    begin
      Result := FSomething;
    end;
    
    class function TDerived1.Get: String;
    begin
      Result := FSomething;
    end;
    
    begin
      TBase.FSomething := 'Base';
      TDerived1.FSomething := 'Derived1';
      TDerived2.FSomething := 'Derived2';
      WriteLn(TDerived1.Get);
      ReadLn;
    end.
    

    为了可读性,我建议重命名 TDerived1 中的私有字段。

    或者,使用class property

    program Project1;
    
    {$APPTYPE CONSOLE}
    
    type
      TBase = class
      private
        class var FSomething: String;
      public
        class property Something: String read FSomething write FSomething;
      end;
    
      TDerived1 = class(TBase)
      private
        class var FAnotherThing: String;
      public
        class property Something: String read FAnotherThing write FAnotherThing;
      end;
    
      TDerived2 = class(TBase);
    
    begin
      TBase.Something := 'Base';
      TDerived1.Something := 'Derived1';
      TDerived2.Something := 'Derived2';
      WriteLn(TDerived1.Something);
      ReadLn;
    end.
    

    【讨论】:

    • 啊,是的,我明白了。所以如果不覆盖父级的 get 方法,就没有机会获得该内存区域?因为如果是这样的话,我猜这个方法应该被声明为“类函数Get:string;virtual;abstract;”或者也许我们应该触摸 vmt 表仍然好奇..
    • (1) 是的,有,看我的第二个解决方案。 (2) 不,因为那样TBase.Get 会引发抽象错误。
    猜你喜欢
    • 2010-12-21
    • 2010-09-07
    • 2023-03-23
    • 2021-08-17
    • 1970-01-01
    • 1970-01-01
    • 2021-01-10
    • 1970-01-01
    相关资源
    最近更新 更多