What is the best way to serialize Delphi application configuration?

后端 未结 7 1735
猫巷女王i
猫巷女王i 2021-02-02 02:21

I will answer this question myself, but feel free to provide your answers if you are faster than me or if you don\'t like my solution. I just came up with this idea and would li

相关标签:
7条回答
  • 2021-02-02 02:40

    I use XML for all my application as means of configuration. It is:

    • flexible
    • future feature proof
    • easy to read with any text reader
    • very easy to extend in application. No class modifications needed

    I have an XML library that makes it extremely easy to read or modify configuration, without even having to watch for missing values. Now you can also map the XML to a class inside application for faster access if speed is the issue, or certain values are read constantly.

    I find other configuration methods far less optional:

    • Ini file: no in depth structure, far less flexible
    • registry: just keep away from that.
    0 讨论(0)
  • 2021-02-02 02:48

    Nicks answer (using Java Properties) has a point: this simple way to read and pass configuration around between parts of the application does not introduce dependencies on a special configuration class. A simple key/value list can reduce the dependencies between application modules and make code reuse easier.

    In Delphi, a simple TStrings-based configuration is an easy way to implement a configuration. Example:

    mail.smtp.host=192.168.10.8    
    mail.smtp.user=joe    
    mail.smtp.pass=*******
    
    0 讨论(0)
  • 2021-02-02 02:50

    This would be for Java.

    I like to use the java.util.Properties class for reading in config files or properties files. What I like is that you put your file with lines in the same way you showed above (key=value). Also, it uses a # (pound sign) for a line thats a comment, kind of like a lot of scripting languages.

    So you could use:

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

    etc

    Then you just have code like:

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

    Easy as that.

    0 讨论(0)
  • 2021-02-02 02:59

    Basically you are asking for a solution to serialize a given object (in your case a configurations to ini files). There are ready made components for that and you can start looking here and here.

    0 讨论(0)
  • 2021-02-02 03:00

    This is my proposed solution.

    I have a base class

    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;
    

    The Load methods look like this (Save method accordingly):

    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;
    

    The base class could provide load and save methods for the delphi default types. I can then create a configuration for my application like this:

    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;
    

    Example of a custom save method:

    function TMyConfiguration.SaveTObject (P : Pointer) : String;
    var
      Obj : TObject;
    begin
      Obj := TObject (P);
      Result := Obj.ClassName;  // does not make sense; only example;
    end;       
    
    0 讨论(0)
  • 2021-02-02 03:00

    My preferred method is to create an interface in my global interfaces unit:

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

    This interface is then "exposed" in my main form:

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

    Most of the time the actual implementation is not in the main form, its just a place holder and I use the implements keyword to redirect the interface to another object owned by the main form. The point of this is that the responsibility of configuration is delegated. Each unit doesn't care if the configuration is stored in a table, ini file, xml file, or even (gasp) the registry. What this DOES allow me to do in ANY unit which uses the global interfaces unit is make a call like the following:

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

    All that is needed is adding FORMS and my global interfaces unit to the unit I'm working on. And because it doesn't USE the mainform, if I decide to later reuse this for another project, I don't have to do any further changes....it just works, even if the configuration storage scheme is completely different.

    My general preference is to create a table (if I'm dealing with a database application) or an XML file. If it is a multi-user database application, then I will create two tables. One for global configuration, and another for user configuration.

    0 讨论(0)
提交回复
热议问题