Блог Александра Божко
Архивы
Рубрики
Поделись с другими!
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

Оригинал.

Этот пост основывается на коде модуля IniPersit.pas, недавно реализованного мною.
Обычно я делаю конфигурационные классы для создания общей и легкодоступной информации, хранящейся в INI, реестре или в XML. В данных примерах я покажу, как использовать RTTI и атрибуты в Delphi 2010, для того, что бы получить новый способ создания конфигурационного класса, имеющего доступ к информации, хранящейся в INI файле.
Давайте, прежде всего, посмотрим, как использовать Новый Модуль, а затем мы сможем приоткрыть завесу, что бы посмотреть, как это работает.

 
unit ConfigSettings;
interface
uses
  IniPersist;

type
  TConfigSettings = class (TObject)
  private
    FConnectString: String;
    FLogLevel: Integer;
    FLogDirectory: String;
    FSettingsFile: String;
  public
    constructor create;
    // Use the IniValue attribute on any property or field
    // you want to show up in the INI File.
    [IniValue('Database','ConnectString','')]
    property ConnectString : String read FConnectString write FConnectString;
    [IniValue('Logging','Level','0')]
    property LogLevel : Integer read FLogLevel write FLogLevel;
    [IniValue('Logging','Directory','')]
    property LogDirectory : String read FLogDirectory write FLogDirectory;

    property SettingsFile : String read FSettingsFile write FSettingsFile;
    procedure Save;
    procedure Load;

  end;

implementation
uses SysUtils;

{ TApplicationSettings }

constructor TConfigSettings.create;
begin
  FSettingsFile := ExtractFilePath(ParamStr(0)) +  'settings.ini';
end;

procedure TConfigSettings.Load;
begin
// This loads the INI File Values into the properties.
   TIniPersist.Load(FSettingsFile,Self);
end;

procedure TConfigSettings.Save;
begin
// This saves the properties to the INI
   TIniPersist.Save(FSettingsFile,Self);
end;

end.
 
program Project13;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  IniPersist,
  ConfigSettings;

var
  Settings : TConfigSettings;

begin
    Settings := TConfigSettings.Create;
    try
      Settings.ConnectString := '\\127.0.0.1\DB:2032';
      Settings.LogLevel := 3;
      Settings.LogDirectory := 'C:\Log';
      Settings.Save;
    finally
      Settings.Free;
    end;

    Settings := TConfigSettings.Create;
    try
    Settings.Load;
    WriteLn(Settings.ConnectString);
    Writeln(Settings.LogLevel);
    Writeln(Settings.LogDirectory);
    finally
      Settings.Free;
    end;
    Readln;
end.

Результат:

 
\\127.0.0.1\DB:2032
3
C:\Log

Результирующий INI файл:

[Database]
ConnectString=\\127.0.0.1\DB:2032
[Logging]
Level=3
Directory=C:\Log

Как вы можете видеть в приведенном выше коде, это, действительно, не слишком сложно , если вы хотите сохранить в INI файле поле или свойство, вы сразу же должны добавить IniValue атрибут.

[Database]
ConnectString=\\127.0.0.1\DB:2032
[Logging]
Level=3
Directory=C:\Log

Конструктор IniValue позволяет вам указать Секцию(Section), Имя (Name) поля или свойства, которое будет сохраняться. Он также позволит вам задать текущее значение если имя и секция не существуют в INI файле.

 
  IniValueAttribute = class(TCustomAttribute)
  private
    FName: string;
    FDefaultValue: string;
    FSection: string;
  published
     constructor Create(const aSection : String;const aName : string;const aDefaultValue : String = '');
     property Section : string read FSection write FSection;
     property Name : string read FName write FName;
     property DefaultValue : string read FDefaultValue write FDefaultValue;
  end;

...

constructor IniValueAttribute.Create(const aSection, aName, aDefaultValue: String);
begin
  FSection := aSection;
  FName := aName;
  FDefaultValue := aDefaultValue;
end;

Вся “магия” действительно находится в TIniPersist.

 
TIniPersist = class (TObject)
private
  class procedure SetValue(aData : String;var aValue : TValue);
  class function GetValue(var aValue : TValue) : String;
  class function GetIniAttribute(Obj : TRttiObject) : IniValueAttribute;
public
  class procedure Load(FileName : String;obj : TObject);
  class procedure Save(FileName : String;obj : TObject);
end;

Методы загрузки и сохранения (Load and Save) почти одинаковы, так, что давайте рассмотрим загрузку.

 
class procedure TIniPersist.Load(FileName: String; obj: TObject);
var
 ctx : TRttiContext;
 objType : TRttiType;
 Field : TRttiField;
 Prop  : TRttiProperty;
 Value : TValue;
 IniValue : IniValueAttribute;
 Ini : TIniFile;
 Data : String;
begin
 ctx := TRttiContext.Create;
 try
   Ini := TIniFile.Create(FileName);
   try
     objType := ctx.GetType(Obj.ClassInfo);
     for Prop in objType.GetProperties do
     begin
       IniValue := GetIniAttribute(Prop);
       if Assigned(IniValue) then
       begin
          Data := Ini.ReadString(IniValue.Section,IniValue.Name,IniValue.DefaultValue);
          Value := Prop.GetValue(Obj);
          SetValue(Data,Value);
          Prop.SetValue(Obj,Value);
       end;
     end;
     for Field in objType.GetFields do
     begin
       IniValue := GetIniAttribute(Field);
       if Assigned(IniValue) then
       begin
          Data := Ini.ReadString(IniValue.Section,IniValue.Name,IniValue.DefaultValue);
          Value := Field.GetValue(Obj);
          SetValue(Data,Value);
          Field.SetValue(Obj,Value);
       end;
     end;
   finally
     Ini.Free;
   end;
 finally
   ctx.Free;
 end;
end;

Как вы можете видеть, мы, по сути, в цикле перебираем все свойства и поля, проверяя атрибуты, и если они существуют, мы берем текущие значения, которые установлены в TypeInfo в TValue объекте. Затем мы определяем строку, возвращаемую из INI файла в TValue, и вызываем SetValue().
Давайте посмотрим на два метода, которые вызываются.

 
class procedure SetValue(aData : String;var aValue : TValue);
class function GetIniAttribute(Obj : TRttiObject) : IniValueAttribute;

Сперва взглянем на SetValue(). Вы можете увидеть, что он зависит от представления TypeInfo, переданного в TValue. Мы проверяем TValue и выполняем необходимое преобразование, для приведения строки (String) к корректному типу, перед   сохранением.

 
class procedure TIniPersist.SetValue(aData: String;var aValue: TValue);
var
 I : Integer;
begin
 case aValue.Kind of
   tkWChar,
   tkLString,
   tkWString,
   tkString,
   tkChar,
   tkUString : aValue := aData;
   tkInteger,
   tkInt64  : aValue := StrToInt(aData);
   tkFloat  : aValue := StrToFloat(aData);
   tkEnumeration:  aValue := TValue.FromOrdinal(aValue.TypeInfo,GetEnumValue(aValue.TypeInfo,aData));
   tkSet: begin
             i :=  StringToSet(aValue.TypeInfo,aData);
             TValue.Make(@i, aValue.TypeInfo, aValue);
          end;
   else raise EIniPersist.Create('Type not Supported');
 end;
end;

Теперь давайте рассмотрим GetIniAttribute(). Цель этого метода проконтролировать и выявить случаи, когда TRttimember (поле или свойство) имеет атрибут IniValue, и если это так – возвратить его, в противном случае возвратить NIL.

 
class function TIniPersist.GetIniAttribute(Obj: TRttiObject): IniValueAttribute;
var
 Attr: TCustomAttribute;
begin
 for Attr in Obj.GetAttributes do
 begin
    if Attr is IniValueAttribute then
    begin
      exit(IniValueAttribute(Attr));  // Exit with a parameter new in Delphi 2010
    end;
 end;
 result := nil;
end;

В общем, это и все, здесь действительно не много кода, и этот код делает использование (RTTI) более простым. В данном случае обработка TIniValue не так уж сложна, но этот подход может быть применен и к другим приложениям.


Поделись с другими!
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

1 комментарий: Представление INI с помощью RTTI

Оставить комментарий

Ваш email не будет опубликован. Обязательные поля отмечены *

Вы можете использовать это HTMLтеги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Продукты DevArt
Купить онлайн:



Читай русскоязычные Delphi блоги
Каталог блогов Blogdir.ru
Яндекс.Метрика