Delphiのインターフェイストラップ
今、私は2つの大きな落とし穴を知っています:
トラップ1。インターフェイスタイプ変換トラップ
a)オブジェクトが実際にこのインターフェイスを実装していても、この参照のタイプを宣言しないインターフェイスにオブジェクト参照をキャストすることはできません(HEHE、利点は発音が困難です)。 b)オブジェクト変数がインターフェイス変数に割り当てられ、インターフェイス変数がオブジェクト変数に割り当てられた場合、オブジェクト変数のアドレスが変更された場合、つまり、元のオブジェクトではなく、エラーアドレスを指します。例:i1 =インターフェイス
関数do:boolean;
終わり;
TC1 =クラス
att1:整数;
終わり;
TC2 =クラス(TC1、I1)
ATT2:整数;
関数do:boolean;
end; intf1:i1; obj1:tc!; obj2:tc2; obj2:= tc2.create;
obj1:= obj2。
I1(obj2).do;
I1(obj1).do; OBJ1のタイプTC1はI1が実装されていることを宣言していないため、OBJ1がI1を実装していても、I1に変換することはできません。また、オブジェクトをインターフェイスに変換してから戻ると、問題が発生します。 obj2:= tc2.create; obj2.att1:= 0;
intf1:= obj2; //正しい。 obj2:= intf1(intf1):= 0; obj2.att1:= 0;つまり、オブジェクトリファレンスからポインターリファレンスに変換した後、アドレスは変更されますが、ポインター参照がオブジェクト参照から戻ってからオブジェクト参照(Delphiのバグ?)に戻るときです。
トラップ2。インターフェイスライフタイム管理
私の常識に基づいて(ここでは、Delphiの使用の常識ではなく、常識をプログラミングしています)、インターフェイスは実際のオブジェクトを生成できないため、インターフェイスには生涯管理を必要としないと思います。しかし、Delphiは再び私の常識を打ちました(Huh、なぜ「更新」と言うのですか?)、そのインターフェイスには寿命があり、次の3つの方法を実装する必要があります。 stdcall _addref:integer _release:integer;したがって、これらの3つの方法を実装する方法がわかりません。 jこれらの3つの方法を自分で実装したくない場合は、tomponentを使用できます。 Tomponentはこれらの3つの方法を実装しているため、そこから継承できるため、これら3つの方法を実装する必要はありません。自信を持って使用できますか?答えはノーです。インターフェイス変数をnilに設定したとき、Delphiは密かに(私の期待を超えていたため)。 function _intfclear(var dest:pointer; var p:= @dest; )._RELEASE; END; function tcomponent._Release:integer; 、そうすることは何もしません。私たちがcomオブジェクトとして機能していないのは、問題はありませんか?答えは、次の状況を考えてみましょう。違法なアドレスアクセスエラーが発生します。なぜ?上記のように、インターフェイス参照がnilに設定されると、_intfclearは呼び出され、_intfclearはこの時点でオブジェクトを呼び出し、自然に違法なアドレスアクセスエラーがあります。一部の人々は、インターフェイスの参照は単なるアドレスであると言います、そして、それを手動でゼロに設定する必要はありません。 obj2:= tc2.create; tryintf1:= obj2; intf1.do; end;結果はまだ予期しない、または違法なアドレスアクセスエラーです。なぜ? Delphiコンパイラは賢いので、このアドレスをNILに設定するのを忘れていると考えているので、Delphiコンパイラはあまりにも賢いようです。それを解決する方法は?方法1:最初にインターフェイス参照をNILに設定し、オブジェクトをリリースします。 intf1:= nil。 pointer(intf1):= nil;私は2番目の方法を使用する傾向があるので、最初に誰をリリースするかを考える必要はありません。さらに、一部の設計パターンでは、インターフェイス参照のみを保持する場合があり、参照オブジェクトをいつリリースする必要があります。たとえば、複合パターンを検討してください。 tcomposite = class(tcomponent、i1)プライベートインターリスト:txcontainer; //「リーフ」のインターフェイス参照を保存するコンテナクラス。パブリック手順(AINTF:I1);明らかにそうではないので、「葉」はこの「合成オブジェクト」オブジェクトよりも間違いなくリリースされるでしょうか?私はそうは思わない。この規制が義務付けられている場合、多くの柔軟性が失われます。したがって、これらのインターフェイス参照がゼロに配置されている場合、オブジェクトがリリースされた後に違法アドレスアクセスエラーを回避するために、元のオブジェクトとの関係はないと確信しています。どの容器を使用することを検討する必要がありますか?配列? tlist? Tinterfacelist?まず第一に、私たちが収容したいのはインターフェイスだからです。しかし、彼に自由になると、それがnilに収容するすべてのインターフェイスを配置します。これはまさに私たちが望んでいないものです。または、無料の前に保存するインターフェイス参照をポインターに変換してから、nilに設定することもできます。 I:= 0 to interlist.count -1 dopointer(interlist.items [i]):= nil;次に、配列を試します。インターリスト:I1の配列;動的配列を放出しないでください。非常に便利なようですが、コンパイラがそれをリリースすると、各要素をnilに設定し、インターフェイスとして、違法な住所アクセスエラーの可能性がまだあります。これは、i:= low(arr)〜high(arr)dopointer(arr [i]):= nilで行うことができます。あなたがそれを使用する時間、そしてあなたはそれをすることを覚えていないかもしれません。最後に、tlistを使用します。ただし、tlistにはポインターがあります。xxx.add(aintf:i1)begin interlist.add(pointer(aintf)); end;リリースする際の特別な処理が必要です。完璧なようですが、そのようなコードを書くとどうなりますか? interlist.add(tc2.create);それは純粋なポインターであるため、インターフェイスに変換されると、インターフェイス参照を参照するオブジェクトのアドレス変換は実行されません(それを行う方法はわかりません)。これは、別の違法アドレスアクセスエラーです。これが唯一の書き込み方法です:interlist.add(pointer(i1(tc2.create));それは少し面倒ですが、これは(私が知っているように)最良の解決策です。転送が最初の呼び出しでエラーが発生することを忘れた場合、エラーを見つける方が簡単だからです(配列の使用と比較して)。したがって、私は次のように提案します。1。TLISTを使用してインターフェイス参照を管理します。 2。追加する場合、オブジェクトをインターフェイスに変換してから、元々インターフェイス参照である場合は、ポインターに直接変換できます。 3。TLISTを使用して管理されていないインターフェイス参照の場合。インターフェイスを参照するには、次の方法で手動でnilに設定する必要があります。さらに、nil。したがって、そこから継承すると、これらの3つの方法を実装するトラブルも節約できます。しかし、そのように実装されています。当時、私はそれにショックを受けました。これが実装されている方法であり、Tomponentから継承するよりも多くのトラブルをもたらします。特に使用しない限り、推奨されません。上記のインターフェイスに関するすべての議論は、一般的に使用される言語レベルのインターフェイスに限定されており、com+インターフェイスに関する議論はありません。 Delphiのこれらの奇妙なインターフェイスの実装は、com+インターフェイスから発展することと多くの関係があります。これは、歴史的な重い負担のために行われた妥協の結果です。