2009-08-18 114 views
10

我會自己回答這個問題,但如果您比我快或者您不喜歡我的解決方案,請隨時提供您的答案。我剛剛提出這個想法,並希望對此有一些看法。序列化Delphi應用程序配置的最佳方式是什麼?

目標:一個可讀的配置類(如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 

問題:什麼是做的最好的方法這個?

回答

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; 

負載的方法是這樣的(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; 

的基類可以提供加載和保存爲德爾福默認類型的方法。然後我就可以創建一個配置我的應用程序是這樣的:一個自定義的保存方法的

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;  
+0

這對我來說看起來相當不錯。是什麼讓你覺得可能有更聰明的解決方案? – 2009-08-21 13:01:03

+0

@Jeroen:在大多數情況下,當我在這裏問我的經驗,我得到了很多聰明的評論,改進和批評的建議:)除此之外,我想分享這段代碼,以便其他人可以受益。 – jpfollenius 2009-08-24 06:25:40

0

這將是對Java。

我喜歡使用java.util.Properties類讀取配置文件或屬性文件。我喜歡的是你用上面顯示的相同方式(key = value)將行放在文件中。 此外,它使用#(磅符號)作爲註釋的行,類似於很多腳本語言。

所以,你可以使用:

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

然後你只需這樣的代碼:

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

易爲。

+0

自從我要求Delphi並沒有什麼幫助,你提出的類是Java特定的... – jpfollenius 2009-08-18 12:45:06

+0

對此抱歉。你其實並沒有指定,所以我給了它一個鏡頭。問題非常普遍。沒有看到你的delphi標籤。 – Nick 2009-08-18 13:31:17

+0

沒問題。不管怎麼說,還是要謝謝你。 – jpfollenius 2009-08-18 13:33:51

1

基本上你要求一個解決方案來序列化一個給定的對象(在你的情況下,配置到ini文件)。有現成的組件,你可以開始尋找herehere

6

我使用XML作爲我的所有應用程序的配置手段。它是:

  • 靈活
  • 將來的功能證明
  • 容易用任何文本閱讀器
  • 很容易的適用範圍擴大到閱讀。不需要類修改

我有一個XML庫,它使得讀取或修改配置非常容易,甚至不需要監視缺少的值。現在,您還可以將XML映射到應用程序內部的類,以便在速度問題或速度不斷讀取某些值的情況下更快地訪問。

我發現其他配置方法遠不如可選:

  • INI文件:未在深層結構,遠不如靈活
  • 註冊表:剛剛從保持距離。
+1

我的工作基本相同,但我使用Delphi的XML數據綁定向導將文件映射到對象。 – 2009-08-18 13:30:29

+1

問題是:我認爲即使用戶也可以很容易地理解INI文件,但並不是每個人都能夠理解XML。我知道我應該爲此設置對話框,但我希望任何人都能夠更改高級設置而不必潛入XML。 – jpfollenius 2009-08-18 13:36:32

+1

XML文件非常易於閱讀。這裏我正在討論基本的XML。只有具有屬性和值(文本)的節點。與INI相比,它幾乎不可讀。好的,你在標籤中有更多的「鍋爐電鍍」。但如果你擔心用戶修改,然後爲他們製作一個GUI。您不能依賴用戶自行修改文件。即使是一個INI。 – Runner 2009-08-18 13:51:47

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; 

所有這一切需要的是增加的形式和我的全球接口單元,以我的工作單位上。而且因爲它不使用mainform,所以如果我決定以後再使用它來做另一個項目時,我不必做任何進一步的修改......即使配置存儲方案完全不同,它也能正常工作。

我的一般偏好是創​​建一個表(如果我正在處理數據庫應用程序)或XML文件。如果它是一個多用戶數據庫應用程序,那麼我將創建兩個表。一個用於全局配置,另一個用於用戶配置。

+0

+1,因爲我同意你寫的大部分內容,差異主要是風格問題。但是現在的答案與這個問題沒有多大關係,我把它理解爲:「編寫一個基類的最佳方式是什麼,它能夠將自身加載並存儲到持久存儲中,而不需要更改後代類」。這至少是OP回答的問題,標題與問題文本不符。儘管如此,smasher應該遵循您的建議,而不是對INI文件解決方案進行硬編碼。 – mghie 2009-08-18 17:25:12

+0

我希望我的配置設置能夠根據需要儘可能縮短時間,因爲它會在每個應用程序中重複使用。儘管我在某種程度上喜歡你的方法,但對我來說不足夠。具有XML後端的簡單全局單例以及訪問它的良好界面在大多數情況下已經足夠了。對於小型簡單應用程序來說已經足夠了。 即使完全面向數據庫的應用程序仍然需要一個簡單的配置文件來訪問該數據:) – Runner 2009-08-18 18:24:05

+0

我完全同意mghie的評論。您提出了一個使用接口和委派的全局變量的替代方案,但您並未提到如何執行序列化。從我的解決方案中提取IPropertyStorer以將配置與具體實現(INI文件/ XML /數據庫)分開是沒有問題的。但是,這並沒有改變關於持久存儲的觀點,只需很少的努力。 – jpfollenius 2009-08-18 19:09:32

0

尼克斯的答案(使用Java屬性)有一個觀點:這個簡單的方式來閱讀和周圍傳遞應用程序不會引入的部件之間的配置依賴於特殊的配置類。一個簡單的鍵/值列表可以減少應用程序模塊之間的依賴關係,並使代碼重用更容易。

在Delphi中,一個簡單的基於TStrings的配置是一種實現配置的簡單方法。例如:

mail.smtp.host=192.168.10.8  
mail.smtp.user=joe  
mail.smtp.pass=******* 
相關問題