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

如何在delphi中将常量字符串组合在一起

  •  11
  • Re0sless  · 技术社区  · 16 年前

    一个应用程序,它使用字符串表示一个项在其生命周期中的不同状态。

    工业工程

    打开, 活跃的, 关闭, 删除,

    等等,现在它们都被硬编码成这样的代码

    MyVar := 'OPEN';
    

    我正在努力改变这一点,因为它可能是一个维护问题,所以我想把它们都改成常数,我本来打算这样做的

    MyVar := STATUS_OPEN;
    

    但我想把它们组合成一个这样的数据结构

    MyVar := TStatus.Open;
    

    在Delphi2007中,最好的方法是什么?

    我知道我可以为此做一个记录,但是如何用值填充它,以便它对系统中的所有对象都可用,而不必每次都创建一个变量并填充值?

    理想情况下,我希望有一个数据结构和值的中心位置,并使它们易于访问(如tstatus.open),而不必每次使用时都将其分配给变量或创建对象。

    我确信有一个简单的解决方案,我只是错过了。有什么想法吗?

    7 回复  |  直到 8 年前
        1
  •  13
  •   Ondrej Kelle    16 年前

    正如Jim提到的,您可以使用类常量或枚举类型:

    type
      TItemStatus = (isOpen, isActive, isClosed);
    const
      ItemStatusStrings: array[TItemStatus] of string = ('Open', 'Active', 'Closed');
    
        2
  •  12
  •   Jim    16 年前

    http://edn.embarcadero.com/article/34324 (“Delphi 7之后的新Delphi语言特性”。

    类常量会很好。从上面的链接:

    type
        TClassWithConstant = class
          public 
            const SomeConst = 'This is a class constant';
        end;
    
    
     procedure TForm1.FormCreate(Sender: TObject);
     begin
       ShowMessage(TClassWithConstant.SomeConst);
     end;
    
        3
  •  8
  •   David Taylor    16 年前

    我个人在几个大型关键任务数据处理平台中广泛使用了tondrej的方法。枚举的好处是,它们可以在应用程序中轻松地传递,非常紧凑(序数类型),在case语句中工作得很好,并且是完全类型安全的。后一点对于维护很重要,因为删除或更改枚举值将导致编译错误(好好想想imho)。

    有些问题需要注意:

    • 更改枚举值的声明顺序将禁止枚举字符串查找数组。

    • 如果在case语句中使用enum(这是一个很好的特性),请确保考虑到正在添加的新值。我通常在案例中添加else并对未知值抛出异常。比破案好多了。

    如果您非常关心第一个gotcha,那么可以在lookup数组中使用一个记录,并在每个记录中包含枚举值,并验证单元初始化的顺序。在经常维护的任务关键型系统中,这多次节省了我的开支。这种方法还可以用于向每个值添加额外的元数据(非常方便的特性)。

    祝你好运!

    unit Unit1;
    
    interface
    
    type
      TItemStatusEnum = (isOpen, isActive, isClosed);
    
      TItemStatusConst = class
        class function EnumToString(EnumValue : TItemStatusEnum): string;
        property OPEN: string index isOpen read EnumToString;
        property CLOSED: string index isClosed read EnumToString;
        property ACTIVE: string index isActive read EnumToString;
      end;
    
    var
      ItemStatusConst : TItemStatusConst;
    
    implementation
    
    uses
      SysUtils;
    
    type
      TItemStatusRec = record
        Enum  : TItemStatusEnum;
        Value : string;
      end;
    
    const
      ITEM_STATUS_LOOKUP : array[TItemStatusEnum] of TItemStatusRec =
        ((Enum: isOpen;   Value: 'OPEN'),
         (Enum: isActive; Value: 'ACTIVE'),
         (Enum: isClosed; Value: 'CLOSED'));
    
    procedure ValidateStatusLookupOrder;
      var
        Status : TItemStatusEnum;
      begin
        for Status := low(Status) to high(Status) do
          if (ITEM_STATUS_LOOKUP[Status].Enum <> Status) then
            raise Exception.Create('ITEM_STATUS_LOOKUP values out of order!');
      end;
    
    class function TItemStatusConst.EnumToString(EnumValue: TItemStatusEnum): string;
      begin
        Result := ITEM_STATUS_LOOKUP[EnumValue].Value;
      end;
    
    initialization
      ValidateStatusLookupOrder;
    end.
    

    更新:

    谢谢你的批评-你是绝对正确的,因为我正在解决 感知 作为问题背后的问题。下面是一个 许多的 更简单的方法是,如果您能够忍受这样的限制,即它必须只能在Delphi2007或更高版本中工作(可能在D2006中工作?)。很难摆脱那些关于向后兼容的烦人想法;)

    type
      ItemStatusConst = record
        const OPEN   = 'OPEN';
        const ACTIVE = 'ACTIVE';
        const CLOSED = 'CLOSED';
      end;
    

    这种方法很简单,与Java或.NET应用程序可能会有类似的感觉。使用语义与预期的一样,将与代码完成一起工作。一个重要的优点是,常量的作用域是记录级别的,因此不会像单元作用域类型那样与其他定义发生冲突。

    Sample usage:
    
        ShowMessage(ItemStatusConst.ACTIVE);
        ShowMessage(ItemStatusConst.CLOSED);
    

    只是为了好玩,我还修改了之前的方法,直接解决了原来的问题,得到了类似的结果。这次我使用了“模拟类属性”(参见 here )使用属性索引器。这显然比记录方法更为复杂,但保留了使用枚举值、集合、字符串的能力,并且还可以扩展以在需要时实现其他元数据功能。我相信这适用于Delphi版本,最早可以追溯到Delphi5 IIRC。

    我在Delphi中创建了一个解析框架来处理 满的 IBM AFPDS 打印数据流语法。这种灵活性是我喜欢在德尔福工作的原因——它就像一把瑞士军刀;)

        4
  •  4
  •   Community CDub    8 年前

    或者你可以合并@ Jim @ TOndrej

    TGlobalConsts = class
    type
      TItemStatus = (isOpen, isActive, isClosed);
    const
      ItemStatusStrings: array[TItemStatus] of string = ('Open', 'Active', 'Closed');
    end;
    

    尽管你可以 或A 记录 真正地。但如果是 您可以添加 类函数 像@ mghie 建议,而只是从 常数数组 . 就我个人而言,我更喜欢在 常数数组 界面 而不是在 功能 尸体在 实施 .

    class function TGlobalConsts.Active: string;
    begin
      Result := ItemStatusStrings[itsActive];
    end;
    
    class function TGlobalConsts.Open: string;
    begin
      Result := ItemStatusStrings[itsOpen];
    end;
    

    当然,有很多方法可以做到这一点。

        5
  •  3
  •   mghie    16 年前

    在Delphi的所有版本中都可以这样做:

    type
      TStatus = class
        class function Active: string;
        class function Open: string;
        ...
      end;
    
      class function TStatus.Active: string;
      begin
        Result := 'ACTIVE';
      end;
    
      class function TStatus.Open: string;
      begin
        Result := 'OPEN';
      end;
    

    你可以随心所欲地使用它:

    MyVar := TStatus.Open;
    

    只有一个地方可以更改字符串,而且只涉及代码,没有运行时实例化。Delphi最新版本的新特性并不总是需要这样做…

        6
  •  2
  •   Remy Lebeau    8 年前

    除了Tondrej说的:

    还可以声明记录常量:

    type
      TRecordName = record
        Field1: Integer;
        Field2: String
      end;
    
    const
      itemstatus : TRecordName = (
        Field1: 0;
        Field2: 'valueoffield2'
      );
    

    通过组合 const 记录上面的语法和tondrej显示的数组语法。

    但是,我通常喜欢的方式需要初始化:

    • 填充A TStringDynArray 通过rtti使用枚举的名称
    • 如果结果证明这些名称应该是可配置的,那么持久性(不管它是如何安排的)可以简单地加载/存储dynarray。
        7
  •  1
  •   eKek0    16 年前

    您可以有一个记录类型的全局变量。记录类型必须为您拥有的每个状态都有一个字段。

    您可以在 初始化 任何单元的一部分,调用在该单元中声明的过程或函数。您可以有一个单独的单元来专门完成这项工作,比如statustypes.pas。

    在接口部分,您可以声明如下内容:

    type 
      TStatus = record
        OPEN: string;
      end;
    
    var
      Status: TStatus;