【问题标题】:How to refer to a class from within a constant array of record?如何从记录的常量数组中引用一个类?
【发布时间】:2014-05-04 23:36:23
【问题描述】:

我有一个 TiffData 类,它包含用于加载和显示数据的抽象方法,以及许多定义这些方法的祖先类,如 TTiffByte、TTiffAscii、TTiffShort。

我还有一个常量数组或记录,可让我根据 IFD 中记录的数据类型查找有关类型的信息。

我想不通,如何存储每个数组元素的关联类。

type
    TtiffType = record
      name  : string;
      bytes : word;
      data  : class;   {  ?  }
      desc  : string;
    end;

const
TiffTypes : array[ 1 .. 18 ] of TTiffType =  (

{01} ( name: 'byte';  bytes : 1; data: TTiffByte;     { ? }
       desc: '8-bit unsigned number.'   ),
{02} ( name: 'ascii';  bytes : 1; data: TTiffAscii;   { ? }
       desc: '8 bit byte that contains a 7 bit ASCII code; the last byte must be NULL (binary zero)'   ),
{03} ( name: 'short';  bytes : 2; data: TTiffShort;   { ? }
       desc: '16-bit (2-byte) unsigned integer.'   ),
...

我想做的是在数组中保存对类的引用:

value := TiffTypes[ fldType ].Data.Create;

value.loadFromOffset( offset );
Edit1.Text := value.getShortString;

或者在数组中保存一个构造函数:

value := TiffTypes[ fldType ].data;

value.loadFromFile( f, offset, count );
InternalRepresentation := TTiffAscii( value.storage );

【问题讨论】:

    标签: class delphi delphi-xe2 record


    【解决方案1】:

    由于您的类有一个公共基类TiffData,您可以在记录中使用class of TiffData,例如:

    type
      TtiffType = record
        name  : string;
        bytes : word;
        data  : class of TiffData; // <-- here
        desc  : string;
      end;
    

    只要TiffData 的构造函数声明为virtual 并且每个派生类都覆盖它,使用Data.Create 就会按预期工作。

    【讨论】:

    • 为什么构造函数需要声明为虚拟的?无论如何都行不通吗?
    • 没有。假设Data 指向TTiffByte。如果TiffData.Create 不是虚拟的,则调用Data.Create 不会调用TTiffByte.Create。使用元类创建对象时,虚拟构造函数非常重要。
    • 现在明白你为什么这么说。我几乎从不在构造函数中放任何东西。我尽可能使用 AfterConstruction 和 BeforeDestruction。我很久以前切换的原因正是因为这个原因。当时我不知道这是什么原因,只是我的构造函数从未被调用:)。为重要信息 +1。
    • 构造函数不需要是虚拟的。无论如何都会创建正确的对象。如果后代需要覆盖它,您只需要它是虚拟的,这与将任何其他方法设为虚拟的标准相同。构造函数在这方面并不特殊。
    【解决方案2】:

    你需要的是一个元类。

    type
      TTiffDataClass = class of TiffData;
    

    然后你可以像这样使用它:

    TTiffType = record
      name  : string;
      bytes : word;
      data  : TTiffDataClass;
      desc  : string;
    end;
    

    如果您有 TiffData 的后代,那么您可以使用它。例如:

    TTiffByte= class(TiffData)
    end;
    

    这应该可行:

    {01} ( name: 'byte';  bytes : 1; data: TTiffByte;     { ? }
           desc: '8-bit unsigned number.'   ),
    

    正如雷米在回答中所说,小心构造函数。

    【讨论】:

    • OP 声明基类名为TiffData,因此您应该在答案中反映这一点,而不是编造一个新的虚构名称。
    【解决方案3】:

    感谢 Remi 和 Graymatter!我使用两个答案中的信息来获得以下结果。几乎任何不同的东西都会发出警告、错误或运行时异常。

    定义许多类来管理各种数据类型,TTiffData 的所有祖先。

    Type
    
    TTiffData = class(TObject)
        constructor create; virtual;
        procedure LoadFromIFD( var aValue; Count : LongWord  ); virtual; abstract;
    end;
    
    TTiffShort = class(TTiffData)
        constructor create; override;
        procedure LoadFromIFD( var aValue; Count : LongWord  ); override;
    end;
    
    TTiffAscii = class(TTiffData)
        constructor create; override;
        procedure LoadFromIFD( var aValue; Count : LongWord  ); override;
    end;
    
    ...
    

    注意:我在子级中使用了“覆盖”而不是“虚拟”来防止“方法 '*' 隐藏基类型 'TTiffData' 的虚拟方法”警告。

    数组如下所示:

    type
        TTiffDataClass = class of TTiffData;
    
        TtiffType = record
          name  : string;
          bytes : word;
          data  : TTiffDataClass;
          desc  : string;
        end;
    
    const
    
    
       TiffTypes : array[ 1 .. 18 ] of TTiffType =  (
    {01} ( name: 'byte';  bytes : 1; data: TTiffShort;
           desc: '8-bit unsigned number.'   ),
    {02} ( name: 'ascii';  bytes : 1; data: TTiffAscii;
           desc: '8 bit byte that contains a 7 bit ASCII code; the last byte must be NULL (binary zero)'   ),
    
    ...
    ); 
    

    用法:

        {  Load }
        Value := TiffTypes[ fldType ].data.Create;
    
        if Indirect then begin
            Value.LoadFromFile( f, Offset, Count );
            end
    
        else begin
            ValueLoadFromIFD( Offset, Count );
            end;
    
    
        { Display }
        aStringGrid.Cells[ 8, i ] := Value.ShortDisplay;
    

    【讨论】:

    • 哎哟。不要使用override 来“防止警告”。 了解它的含义并相应地使用它。
    • 您不是在阻止警告,而是在阻止您的覆盖方法不被调用。
    猜你喜欢
    • 1970-01-01
    • 2023-01-20
    • 1970-01-01
    • 2011-12-27
    • 2014-09-29
    • 1970-01-01
    • 2016-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多