我的应用程序使用字符串表示项目在其生命周期内的不同状态。
即
OPEN, 活性, 关闭, DELETE,
依此类推,目前它们都被硬编码成代码,如此
MyVar := 'OPEN';
我正在努力改变它,因为它可能是一个维护问题,所以我想将它们全部改为常量,我打算这样做
MyVar := STATUS_OPEN;
但我想把它们组合成一个像这样的数据结构
MyVar := TStatus.Open;
在delphi 2007中执行此操作的最佳方法是什么?
我知道我可以为此创建一个记录,但是如何使用这些值填充它,以便系统中的所有对象都可以使用它,而无需每次创建变量并填充值?
理想我希望有一个数据结构和值的中心位置,并且可以轻松访问它们(如TStatus.Open),而无需在每次使用时将其分配给变量或创建对象。
我确信有一个简单的解决方案,我只是错过了。任何想法?
答案 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)
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显示的数组语法,也可以实现记录数组。
但是,我通常喜欢的方式需要初始化:
TStringDynArray
答案 6 :(得分:1)
您可以拥有记录类型的全局变量。记录类型必须包含您拥有的每个状态的字段。
您可以在任何单元的 Initialize 部分填充记录,调用在该单元中声明的过程或函数。你可以有一个单独的单元来专门完成这项工作,比如StatusTypes.pas。
在界面部分,您可以声明如下内容:
type
TStatus = record
OPEN: string;
end;
var
Status: TStatus;