ラベル 2010 の投稿を表示しています。 すべての投稿を表示
ラベル 2010 の投稿を表示しています。 すべての投稿を表示

2009年10月7日水曜日

Rttiを使ってClientDataSetを作ってみる

以前、Team JapanのブログにEmployeeクラスのインスタンスからInsert文を
生成するサンプル
のポストがありましたが、ちょっと改良してTClientDataSetを
動的生成する例を書いてみた。(って使い道があるかちょっと疑問です。)

一応ソースは、こんな感じ

unit Unit3;

interface

uses
  DBClient;

type DataSetOperator = Record
function CreateDataSet(obj : TObject) : TClientDataSet;
function AddRecord(cds : TClientDataSet; obj : TObject) : Boolean;
End;


implementation

uses
Rtti,TypInfo, DB, SysUtils;
{ DataSetFactory }

function DataSetOperator.AddRecord(cds: TClientDataSet; obj: TObject): Boolean;
var
ctx : TRttiContext;
rtp : TRttiProperty;
rtps : TArray;
begin

ctx := TRttiContext.Create;

rtps := ctx.FindType(obj.UnitName + '.' + obj.ClassName).GetProperties;
cds.Append;
for rtp in rtps do
begin
cds.FieldByName(rtp.Name).Value := rtp.GetValue(obj).AsVariant;
end;
cds.UpdateRecord;
end;

function DataSetOperator.CreateDataSet(obj: TObject): TClientDataSet;
var
ctx : TRttiContext;
rtp : TRttiProperty;
rtps : TArray;
cds : TClientDataSet;
ft : TFieldType;
fn : String;
ftSize : Integer;
//ftdf : TFieldDef;
begin

cds := TClientDataSet.Create(nil);

ctx := TRttiContext.Create;

rtps := ctx.FindType(obj.UnitName + '.' + obj.ClassName).GetProperties;

for rtp in rtps do
begin
//ここがちょっとダサイかも
//DelphiのRTTIの型とDBの型のマッチング
ftSize := 0;
if CompareText(rtp.PropertyType.Name,'Integer') = 0 then ft := ftInteger;
if CompareText(rtp.PropertyType.Name,'String') = 0 then
begin
ft := ftString;
ftSize := 50;
end;
if CompareText(rtp.PropertyType.Name,'TDateTime') = 0 then ft := ftDateTime;
if CompareText(rtp.PropertyType.Name,'Currency') = 0 then ft := ftCurrency;
//if CompareText(rtp.PropertyType.Name,'Currency') = 0 then ft := ftCurrency;

cds.FieldDefs.Add(rtp.Name,ft,ftSize);

end;
cds.CreateDataSet;
Result := cds;
//cds.FieldDefs.Add();

end;

end.

DBのデータ型の列挙とRTTIのデータ型の列挙が微妙に違っているので
そのマッピングを少々強引に行ってます。

で、上のユニットを利用する例が


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, DB, Grids, DBGrids;

type
TForm1 = class(TForm)
DBGrid1: TDBGrid;
DataSource1: TDataSource;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;

var
Form1: TForm1;

implementation

uses Unit2, Unit3,DBClient;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
Emp : TEmployee;
DF : DataSetOperator;
cds : TClientDataSet;
begin

Emp := TEmployee.Create;

cds := DF.CreateDataSet(Emp);
cds.Active := true;
DataSource1.DataSet := cds;
//DataSource1.DataSet.Active := true;

Emp.EmpNo := 2;
Emp.FirstName := 'OldTPFun';
Emp.LastName := 'Delphi';
Emp.HireDate := StrToDate('2009/10/07');
Emp.Salary := 100000.00;
DF.AddRecord(cds,Emp);

Emp.Free;
end;

end.


このプログラム上で使っているEmployee型の定義は以下のとおりです。



unit Unit2;

interface

type TEmployee = class
private
FEmpNo: Integer;
FFirstName: String;
FLastName: String;
FHireDate: TDateTime;
FSalary: Currency;
procedure SetEmpNo(const Value: Integer);
procedure SetFirstName(const Value: String);
procedure SetLastName(const Value: String);
procedure SetHireDate(const Value: TDateTime);
procedure SetSalary(const Value: Currency);
public
property EmpNo : Integer read FEmpNo write SetEmpNo;
property FirstName : String read FFirstName write SetFirstName;
property LastName : String read FLastName write SetLastName;
property HireDate : TDateTime read FHireDate write SetHireDate;
property Salary : Currency read FSalary write SetSalary;
end;
implementation

{ TEmployee }

procedure TEmployee.SetEmpNo(const Value: Integer);
begin
FEmpNo := Value;
end;

procedure TEmployee.SetFirstName(const Value: String);
begin
FFirstName := Value;
end;

procedure TEmployee.SetHireDate(const Value: TDateTime);
begin
FHireDate := Value;
end;

procedure TEmployee.SetLastName(const Value: String);
begin
FLastName := Value;
end;

procedure TEmployee.SetSalary(const Value: Currency);
begin
FSalary := Value;
end;

end.

2009年9月3日木曜日

TValue型を試してみる

Rttiユニットで新たに定義されたTValue型は、汎用に使えるデータ型に
なっていて、Delphiで使う基本的な型は、Implicit演算子が定義されており
代入可能になっている。

で、実験してみた。以下ソースコード



program Project1;
{$APPTYPE CONSOLE}

uses
SysUtils,
rtti,
typinfo;

var
tv: TValue;
obj: TObject;
intary: Array of TValue;

begin
try
{ TODO -oUser -cConsole Main : ここにコードを記述してください }
// 先ずは何も入れない場合
if tv.IsEmpty then
begin
writeln('TValueはからです');
end;

tv := 100;
writeln('TValueは' + tv.TypeInfo.Name + 'です。');

tv := 100.0;
writeln('TValueは' + tv.TypeInfo.Name + 'です。');

tv := 'saka';

writeln('TValueは' + tv.TypeInfo.Name + 'です。');

obj := TObject.Create;
tv := obj;
writeln('TValueは' + tv.TypeInfo.Name + 'です。');

tv := obj.ClassType;
writeln('TValueは' + tv.TypeInfo.Name + 'です。');

obj.Free;

end.



で実行した様子が下図。

2009年8月29日土曜日

Rttiを試してみるその4

以前のポストで、可視性がPrivateのメソッドは、読めないということを
記述しましたが、実際には、

自分で定義したメソッドであれば

コンパイラ指定{$RTTI EXPLICIT METHODS}で
TRttiContextのGetMehtodで列挙する可視性を制御することが可能のようです。

{$RTTI EXPLICIT METHODS([vcPublished,vcPublic,vcProtected,vcPrivate])}

とすれば、すべての可視性の列挙が可能です。(正し、継承元のメソッドには
可視性の制御は及ばない見たいです。)

また、

{$RTTI EXPLICIT METHODS([vcPrivate])}

とすれば、Private可視性のメソッドの列挙が可能です。

以下、検証ように使ったソースです。


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, CheckLst;

type
TForm1 = class(TForm)
Button1: TButton;
CheckListBox1: TCheckListBox;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;

var
Form1: TForm1;

implementation

uses Rtti,Typinfo,Unit2;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
ctx : TRttiContext;
rtmary : TArray;
rtm : TRttiMethod;
obj : TObject;
Args : Array of TValue;
rtp : TRttiType;
idx : Integer;
begin

ctx := TRttiContext.Create;

//自分で定義したメソッドのみを取得するには、
//GetDeclaredMethodsを使います。
rtmary := ctx.GetType(Unit2.TSakaTest).GetDeclaredMethods;
//rtmary := ctx.GetType(Unit2.TSakaTest).GetMethods();

CheckListBox1.Clear;

if rtmary <> nil then
begin
for rtm in rtmary do
begin
idx := CheckListBox1.Items.Add(rtm.Name);
if rtm.Visibility = TMemberVisibility.mvPrivate then
begin
CheckListBox1.Checked[idx] := true;
end;

end;

end;

end;

end.






unit Unit2;
interface

uses classes;

// {$RTTI EXPLICIT METHODS([vcPublished,vcPublic,vcPrivate])}
{$RTTI EXPLICIT METHODS([vcPrivate])}
Type TSakaTest = Class(TObject)
private
function SayPrivateMessage : String;
public
function SayPublicMessage : String;
End;

implementation

{ TSakaTest }

function TSakaTest.SayPrivateMessage: String;
begin
Result := 'This is a Private Method';
end;

function TSakaTest.SayPublicMessage: String;
begin
Result := 'This is a Public Method';
end;

end.

ユニットを使うダイアログ


Unitの追加先を選択できるようになってたんだ。
気づかなかった。