用RTTI处理程序配置信息
一般来说,程序多少都会有一些自己的配置信息要保存,不论是通常用的保存到INI还是注册表,或者是XML甚至YAML,总归是要写不少代码处理的。
问题的麻烦在于,程序中实现操作配置信息通常并不需要关注它是保存在什么地方,以什么格式保存,但是传统的方法不论是调用TIniFile还是TRegistry或者是DOM,都是需要开发者花一些时间精力浪费在这上面。
我前一阵写个小程序,不高兴弄配置这些麻烦事,于是利用前些年写的一个框架代码,实现了一套通用的配置序列化机制,这样配置信息管理起来就方便多了。
代码详见MDConfig单元。
基本组成是这样:
TMCustomConfigSection 类实现了IMSerializable接口(定义在框架的MDComm中),意味着它可以被递归序列化——不过在已实现的INI文件和注册表文件序列化 中,这种递归仅限于一层:即一个配置文件可以包括多个配置段,但是配置段下不能再有配置段。当然,框架中实现的通用序列化(二进制和XML)不受此限。
TMCustomConfig类则是可序列化的配置文件,派生自TPersistent。这个类里除了可以包含配置信息以外,还可以包括配置段。
TMCustomConfigSerializer类则是配置序列化基类,实现配置序列化的公共部分——即与具体序列化方式无关的部分。
TMIniSerializer类是INI文件的序列化类,通过它可以把配置文件序列化到INI文件中去。
TMRegSerializer类是注册表序列化类。
配置的基本使用方法是这样的:
分别从TMCustomConfigSection和TMCustomConfig派生自己的配置段类和配置类,在其中定义配置项目供程序使用。示例代码如:
TMTestConfigSection = class(TMCustomConfigSection)
Private
FStringItem : String;
FIntItem : Integer;
FFloatItem : Double;
FDateItem : TDateTime;
Published
Property StringItem : String Read FStringItem Write FStringItem;
Property IntItem : Integer Read FIntItem Write FIntItem;
Property FloatItem : Double Read FFloatItem Write FFloatItem;
Property DateItem : TDateTime Read FDateItem Write FDateItem;
End;
TMTestConfig = class(TMCustomConfig)
Private
FSectionItem : TMTestConfigSection;
FStringItem : String;
public
Constructor Create(aDefaultSerializer : IMSerializer = Nil);
destructor Destroy; Override;
Published
Property SectionItem : TMTestConfigSection Read FSectionItem Write FSectionItem;
Property StringItem : String Read FStringItem Write FStringItem;
End;
Constructor TMTestConfig.Create(aDefaultSerializer : IMSerializer = Nil);
begin
Inherited Create(aDefaultSerializer);
FSectionItem := TMTestConfigSection.Create();
end;
destructor TMTestConfig.Destroy;
begin
FSectionItem.Free;
Inherited
end;
序 列化方式则可以自由选择目前已经实现的四种:INI文件,注册表,二进制文件,XML。序列化操作也可以选择默认序列化或是指定序列化。默认序列化就是在 Config对象构造时传入默认的序列化方式,指定序列化就是在调用Config对象的Load/Save方法时指定序列方式。当然,即使设置了默认序列 化方式,也是可以在调用Load/Save方法时重新指定的。
默认序列化:
conf := TMTestConfig.Create(TMIniSerializer.Create('e:\temp\test.ini'));
conf.StringItem := 'This is default section item';
conf.SectionItem.StringItem := 'This is Section item';
conf.SectionItem.IntItem := 12345;
conf.SectionItem.FloatItem := 9.87654;
conf.SectionItem.DateItem := EncodeDate( 2009, 01, 02 );
conf.Save;
指定序列化:
conf := TMTestConfig.Create;
conf.Load(TMIniSerializer.Create('e:\temp\test.ini'));
conf.Save(TMRegSerializer.Create(HKEY_CURRENT_USER, 'software\mental studio\devotee\test'));
注意,其中序列化器是通过引用计数维护的,会自动释放,不需要显示释放。
只使用配置序列化部分的话,只需要MDComm和MDConfig两个单元文件(二进制和XML序列化需要MSerializ单元)。
在BCB中使用的特别注意事项:
要用一个Delphi单元来定义配置类,因为用到了Delphi的RTTI,虽然用C++来写配置类也可以编译,但是会发生运行时错误。特别关键的是序列化器对象必须在Delphi单元中创建。形如:
Constructor TMainConfig.Create(aIniName : String);
begin
Inherited Create(TMIniSerializer.Create(aIniName, 'Default'));
end;
// 然后在BCB中这样使用
TMainConfig * MainCfg = new TMainConfig(ChangeFileExt(Application->ExeName,".ini"));