10

我会自己回答这个问题,但如果你比我快或者你不喜欢我的解决方案,请随时提供你的答案。我只是想出了这个想法,并想对此发表一些意见。

目标:一个可读的配置类(如 INI 文件),但无需编写(并在添加新配置项后适应)加载和保存方法。

我想创建一个类

TMyConfiguration = class (TConfiguration)
  ...
  property ShowFlags : Boolean read FShowFlags write FShowFlags;
  property NumFlags : Integer read FNumFlags write FNumFlags;
end;

调用 TMyConfiguration.Save(从 TConfiguration 继承)应该创建一个类似的文件

[Options]
ShowFlags=1
NumFlags=42

问题:最好的方法是什么?

4

7 回答 7

7

这是我提出的解决方案。

我有一个基类

TConfiguration = class
protected
  type
    TCustomSaveMethod = function  (Self : TObject; P : Pointer) : String;
    TCustomLoadMethod = procedure (Self : TObject; const Str : String);
public
  procedure Save (const FileName : String);
  procedure Load (const FileName : String);
end;

Load 方法如下所示(相应的 Save 方法):

procedure TConfiguration.Load (const FileName : String);
const
  PropNotFound = '_PROP_NOT_FOUND_';
var
  IniFile : TIniFile;
  Count : Integer;
  List : PPropList;
  TypeName, PropName, InputString, MethodName : String;
  LoadMethod : TCustomLoadMethod;
begin
  IniFile := TIniFile.Create (FileName);
  try
    Count := GetPropList (Self.ClassInfo, tkProperties, nil) ;
    GetMem (List, Count * SizeOf (PPropInfo)) ;
    try
      GetPropList (Self.ClassInfo, tkProperties, List);
      for I := 0 to Count-1 do
        begin
        TypeName  := String (List [I]^.PropType^.Name);
        PropName  := String (List [I]^.Name);
        InputString := IniFile.ReadString ('Options', PropName, PropNotFound);
        if (InputString = PropNotFound) then
          Continue;
        MethodName := 'Load' + TypeName;
        LoadMethod := Self.MethodAddress (MethodName);
        if not Assigned (LoadMethod) then
          raise EConfigLoadError.Create ('No load method for custom type ' + TypeName);
        LoadMethod (Self, InputString);
        end;
    finally
      FreeMem (List, Count * SizeOf (PPropInfo));
    end;
  finally
    FreeAndNil (IniFile);
  end;

基类可以为 delphi 默认类型提供加载和保存方法。然后我可以为我的应用程序创建一个配置,如下所示:

TMyConfiguration = class (TConfiguration)
...
published
  function  SaveTObject (P : Pointer) : String;
  procedure LoadTObject (const Str : String);
published
  property BoolOption : Boolean read FBoolOption write FBoolOption;
  property ObjOption : TObject read FObjOption write FObjOption;
end;

自定义保存方法的示例:

function TMyConfiguration.SaveTObject (P : Pointer) : String;
var
  Obj : TObject;
begin
  Obj := TObject (P);
  Result := Obj.ClassName;  // does not make sense; only example;
end;       
于 2009-08-18T12:35:02.380 回答
6

我在所有应用程序中都使用 XML 作为配置手段。这是:

  • 灵活的
  • 未来功能证明
  • 易于使用任何文本阅读器阅读
  • 很容易在应用中扩展。无需修改类

我有一个 XML 库,它使读取或修改配置变得非常容易,甚至无需注意缺失值。现在,您还可以将 XML 映射到应用程序内部的类,以便在速度问题或不断读取某些值时更快地访问。

我发现其他配置方法的可选性要少得多:

  • ini 文件:没有深度结构,远没有那么灵活
  • 注册表:远离那个。
于 2009-08-18T13:29:15.027 回答
3

我首选的方法是在我的全局接口单元中创建一个接口:

type
  IConfiguration = interface
    ['{95F70366-19D4-4B45-AEB9-8E1B74697AEA}']
    procedure SetConfigValue(const Section, Name,Value:String);
    function GetConfigValue(const Section, Name:string):string;
  end;

然后这个界面以我的主要形式“暴露”:

type
  tMainForm = class(TForm,IConfiguration)
  ...
  end;

大多数情况下,实际的实现不在主窗体中,它只是一个占位符,我使用 implements 关键字将接口重定向到主窗体拥有的另一个对象。这一点是配置的责任被委派了。每个单元都不关心配置是否存储在表、ini 文件、xml 文件甚至(喘气)注册表中。这允许我在使用全局接口单元的任何单元中执行如下调用:

var
  Config : IConfiguration;
  Value : string;
begin
  if Supports(Application.MainForm,IConfiguration,Config) then
    value := Config.GetConfiguration('section','name');
  ...      
end;

所需要的只是将 FORMS 和我的全局接口单元添加到我正在处理的单元中。而且因为它不使用主窗体,所以如果我决定稍后将它重用于另一个项目,我不必做任何进一步的更改......它就可以工作,即使配置存储方案完全不同。

我的一般偏好是创​​建一个表(如果我正在处理一个数据库应用程序)或一个 XML 文件。如果它是一个多用户数据库应用程序,那么我将创建两个表。一个用于全局配置,另一个用于用户配置。

于 2009-08-18T15:58:34.123 回答
1

基本上,您要求一种解决方案来序列化给定对象(在您的情况下是对 ini 文件的配置)。有现成的组件,你可以开始在这里这里寻找。

于 2009-08-18T12:40:04.237 回答
1

前段时间我为同一个任务写了一个小单元——在 xml 文件中保存/加载应用程序的配置。

检查我们的免费软件 SMComponent 库中的 Obj2XML.pas 单元: http ://www.scalabium.com/download/smcmpnt.zip

于 2009-08-19T14:59:04.560 回答
0

这将适用于 Java。

我喜欢使用java.util.Properties类来读取配置文件或属性文件。我喜欢的是您以与上面显示的方式相同的方式将文件放入行中(键=值)。此外,它使用#(井号)作为注释行,有点像许多脚本语言。

所以你可以使用:

ShowFlags=true
# this line is a comment    
NumFlags=42

ETC

然后你只有这样的代码:

Properties props = new Properties();
props.load(new FileInputStream(PROPERTIES_FILENAME));
String value = props.getProperty("ShowFlags");
boolean showFlags = Boolean.parseBoolean(value);

就这么简单。

于 2009-08-18T12:39:08.403 回答
0

尼克斯的回答(使用 Java 属性)有一点:这种在应用程序的各个部分之间读取和传递配置的简单方法不会引入对特殊配置类的依赖关系。一个简单的键/值列表可以减少应用程序模块之间的依赖关系,使代码复用更容易。

在 Delphi 中,简单的基于 TStrings 的配置是实现配置的简单方法。例子:

mail.smtp.host=192.168.10.8    
mail.smtp.user=joe    
mail.smtp.pass=*******
于 2009-08-22T07:33:18.880 回答