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

时间:2009-05-29 16:13:43

标签: delphi

我的应用程序使用字符串表示项目在其生命周期内的不同状态。

OPEN, 活性, 关闭, DELETE,

依此类推,目前它们都被硬编码成代码,如此

MyVar := 'OPEN';

我正在努力改变它,因为它可能是一个维护问题,所以我想将它们全部改为常量,我打算这样做

MyVar := STATUS_OPEN;

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

MyVar := TStatus.Open;

在delphi 2007中执行此操作的最佳方法是什么?

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

理想我希望有一个数据结构和值的中心位置,并且可以轻松访问它们(如TStatus.Open),而无需在每次使用时将其分配给变量或创建对象。

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

7 个答案:

答案 0 :(得分:13)

正如Jim所说,你可以使用类常量或枚举类型:

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

答案 1 :(得分:12)

参见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;

答案 2 :(得分:8)

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

有些人需要注意这种方法:

  • 更改枚举值的声明顺序将会阻止enum->字符串查找数组。

  • 如果在case语句中使用枚举(一个不错的功能),请务必考虑要添加的新值。我通常在case中添加一个else并在未知值上抛出异常。比通过这个案子好多了。

如果您非常关心第一个问题,可以在查找数组中使用记录并在每个记录中包含枚举值,并从单元初始化中验证排序。这使我的培根在经常进行维护的关键任务系统中多次保存。此方法还可用于向每个值添加其他元数据(非常方便的功能)。

祝你好运!

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.

更新

感谢您的批评 - 您完全正确,因为我正在解决我认为这个问题背后的问题。下面是一个很多更简单的方法,如果你能忍受这个必须只能在Delphi 2007或更高版本中工作的约束(可能在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 5 IIRC。

我使用这种技术效果很好的一个例子是我在Delphi中创建的解析框架,用于处理完整的 IBM AFPDS打印数据流语法。这种灵活性是我喜欢在德尔福工作的原因 - 它就像一把瑞士军刀;)

答案 3 :(得分:4)

或者您可以合并@ Jim和@ TOndrej

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

虽然你可以真的让它成为记录。虽然如果它是,您可以添加类函数,如建议的@ mghie,而只是从 const数组中获取值。我个人更喜欢在接口中使用 const数组中的所有字符串,而不是在实现中的函数主体中添加强>

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

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

有很多方法可以做到,这是肯定的。

答案 4 :(得分:3)

您可以在所有版本的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版本的新功能并不总是需要这样做......

答案 5 :(得分:2)

除了TOndrej所说的话:

还可以声明记录常量:

type
  TRecordName = record
    Field1: Integer;
    Field2: String
  end;

const
  itemstatus : TRecordName = (
    Field1: 0;
    Field2: 'valueoffield2'
  );

通过组合上面的const记录语法和TOndrej显示的数组语法,也可以实现记录数组。

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

  • 通过RTTI
  • 填充带有枚举名称的TStringDynArray
  • 如果事实证明名称应该是可配置的,那么持久性(无论它是否被安排)可以简单地加载/存储dynarray。

答案 6 :(得分:1)

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

您可以在任何单元的 Initialize 部分填充记录,调用在该单元中声明的过程或函数。你可以有一个单独的单元来专门完成这项工作,比如StatusTypes.pas。

在界面部分,您可以声明如下内容:

type 
  TStatus = record
    OPEN: string;
  end;

var
  Status: TStatus;