「Strong Delphi RTTI-複数の開発言語を理解する必要性に関する議論」という記事で、DelphiのRTTIを使用してデータセットの簡単なオブジェクト化を実装したと言いました。この記事では、私の実装方法を詳細に紹介します。
簡単な例から始めましょう。Roswenデータベースに接続するAdodatasetコントロールがあり、SQLが次のとおりです。
[従業員から]を選択します
次に、コンテンツにlistViewにコンテンツに4つのフィールド、employeeID、FirstName、LastName、および生年月日を表示する必要があります。従来のコードは次のとおりです。
adodataset1は、listview1で開始されますが、inttostr( 'employeed').subitems.add( 'firstname'); fieldbyname(asstring);
ここにはいくつかの主な問題があります:
1。まず、非常に冗長なコードがたくさんあります。たとえば、FieldByNameやASXXXなど、特にASXXXなど、各フィールドの種類を常に覚えておく必要があります。さらに、一部の互換性のないタイプを自動的に変換できない場合、実行時までエラーは見つかりません。
2。ループ内の現在のレコードの動きを自分で処理する必要があります。上記のように、それ以外の場合は、この問題が簡単に対処できますが、プログラマーはそのような小さな詳細に巻き込まれるべきではありません。
3.最も重要なことは、フィールド名が文字列パラメーターを容易に書かれている場合、ランタイムまで発見されないことです。 FieldByname、そのような問題は、顧客に遅れている場合にのみ表示される可能性があります。この種の間違ったフィールド名は、特にプログラムが複数のテーブルを使用する場合、異なるテーブルのフィールド名を簡単に混同するのが簡単です。
OOによって支配されたこの時代、データセットに関連する操作に遭遇したとき、私たちはまだ上記のリレーショナルデータベースの詳細に該当する必要があります。もちろん、それらを取り除く方法、つまりO/Rマッピングもありますが、O/Rマッピングは結局、特にいくつかの小さなアプリケーションでは、誇張する必要はありません。この場合、必要なのは簡単なデータセットオブジェクト化スキームだけです。
Javaやその他のダイナミック言語に触発された私は、Delphiの強力なRTTIを使用してこのシンプルなデータセットオブジェクト化スキームを実装することを考えました。以下は、従来のコードと同じ関数を実装するデータセット客観化アプリケーションコードです。
タイプTDSPEMPLAYEEEES(TMDATASETPROXY)Property EmployeID:Integer Write Setinteger:String Index 1を読み取りますsetvariant;手順tform1.listclick(tobject); (emp.firstname); Emp.Free;
使用法は非常に簡単です。最も重要なことは、最初にプロキシクラスを定義することです。プロキシクラスは、公開された属性を使用してタイプを含むすべてのフィールドを定義することです。次に、オブジェクトの方法でデータセットを操作できます。このプロキシクラスは、RTTIを使用して、属性操作からフィールド操作までのマッピングを実装するだけで、単に対応するユニットを使用します。このクラスの実装単位については、以下で詳しく説明します。
表面には、データセットを定義する追加のプロキシクラスがあります。これはより多くのコードを持っているようですが、これは特にプログラムが同じデータセットの構造を何度も再利用する必要がある場合、コードです。量が大幅に削減されます。さらに、このプロキシクラスの定義は非常に簡単です。属性アクセス関数getXxx/setXXXがすべてベースクラスのTMDATASETPROXYに実装されています。
次に、元のコードに対応するループを見てみましょう。
1。FieldByNameとASXXXは必要ありません。さらに、プロキシクラスで属性操作になりました。それ。間違ったタイプを使用すると、コンパイル中にエラーが報告されます。
2。foreachを使用してレコードトラバーサルを実行すると、次に起因する悪質なループを忘れることを心配する必要がなくなります。
3.最大の利点は、フィールド名がプロパティになることです。そのため、フィールド名がプロキシクラスを定義するときに誤って書かれていない場合、フィールド名の確認を享受できます。
次に、tmdatasetproxyの議論を開始します。その実装のコードは次のとおりです。
(********************************************* ******************** RTTIで実装されたデータセットプロキシは、Mental Studio.Author:Raptorによる著作権を単純に客観化できます日付:1月28日 - 05 ********************************* ****************)ユニットMDSPCOMM;インターフェイスクラス、db、typing = class(tobject)private fpropcount:integer:pproplist; (Aindex:aindex:ppropinfo)プロパティは、getpropsを読むvirtual;仮想float:avalue:avarue:avalue:index:avalue:avalue: :Tdataset); classinfo)fproplist:= fproplist、fpropcount *(pointer)(aobj.classinfo、fproplist); (fproplist)freemem(fproplist; tmproplist.getPropname(aindex:integer):begin result(aindex)^。name; {tmrefdataset} tmdatasetproxy.create(adataset:tdataset);フルーピング:fdatoly.freed.close; end; procedure tmdatasetproxy.beginedit; begin if(fdataset.state <> dsedit)and(fdataset.state <> dsinsert)then fdataset.edit; end; procedure tmdatasetproxy.endit; .state = dsinsert)then fdataset.post; end; function tmdatasetproxy.getinteger(aindex:integer):fdataset.fieldbyname(fproplist.propnames [aindex]).asinteger; :integer):double; begin result:= fdataset.fieldbyname(fproplist.propnames [aindex]).asfloat; end; function tmdatasetproxy.getString(aindex:integer):string; begin result:= fdataset.fieldbyname(fproplist。 aindex]) )fdataset.fieldbyname(aindex]。= avalue; ] .asfloat:= avalue; end; procedure tmdatasetproxy.setstring(aindex:integer:string); fdat.fieldbyname(fproplist.propnames [aindex]).asstring:= avalue; aindex:variant); endit. not fdataset.eof。
TMProplistクラスは、RTTIの属性操作のいくつかの関数のカプセル化です。その機能は、TypinfoユニットのDelphiによって定義されたいくつかのRTTI関数を使用して、公開されたプロパティリスト情報を維持するために、tpersistent派生クラスを実装することです。プロキシクラスは、この属性リストを介して属性名を取得し、最後にこの属性名とデータセット内の対応するフィールドを介して動作します。
TMDATASETPROXYは、データセットプロキシクラスのベースクラスです。最も重要な部分は、アフターコンストラクションでプロパティリストを作成することです。
属性の操作は、整数、二重/フロート、文字列、バリアントの4つのデータ型のみを実装します。必要に応じて、他のデータ型の実装を実装するために独自のプロキシベースクラスを導き出すことができます。それの交換の実装。ただし、あまり一般的に使用されていないタイプの場合、実装する前に実際のプロキシクラスを定義することをお勧めします。たとえば、前の例では、TDateTimeが一般的に使用されるタイプではないと仮定すると、これを行うことができます。
tdspemployee = class(tmdatasetproxy)getDateTime(const Index:integer) GetString Write SetString:String Index 2を読み取ります(getvariant(index)); end; procedure tdspemployee.setDateTime(const indexer; const Value:tdateTime);
このようにして、生年月日をtdateTimeタイプとして直接使用できます。
さらに、これを利用して、いくつかのカスタム特別なデータ型に統一された操作を提供することができます。
また、dataset.editの使用を忘れて引き起こされるランタイムエラーを回避するために、すべてのsetxxxの前にbegineditが呼び出されました。
Foreachは再利用可能になるように実装されます。さらに、Endeditは、変更を自動的に送信する前に呼び出されます。
このデータセットオブジェクトスキームは、現在の最大の問題は、属性のインデックスパラメーターが定義されているときに厳密に順番になければならないことです。これは、DelphiがNative Development Languageであるため、GetXxx/SetXXXを呼び出すときに同じタイプの異なるプロパティを区別する唯一の方法があり、このインデックスパラメーターはコンパイル時に正確に渡されます。ダイナミックはないため、現在の方法は登録してのみ使用できます。