2016-04-12 8 views
3

私は2つの異なるメソッドポインタを持っています。互換メソッドポインタ間のキャスト方法は?

type 
    TComponentMethod = procedure(const AComponent: TComponent) of object; 
    TFormMethod = procedure(const AForm: TForm) of object; 

唯一の違いは、引数の型であるが、それはビューの呼び出し規約ポイントから任意の違いを作るべきではありませんので、両方がオブジェクト参照です。

(ただし、それが原因でコ/ contravarianceの、型の安全性の問題である可能性があります。)

type 
    TForm1 = class(TForm) 
    procedure FormCreate(Sender: TObject); 
    private 
    procedure M2(const AForm: TForm); 
    end; 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    FormMethod: TFormMethod; 
    ComponentMethod: TComponentMethod; 
begin 
    FormMethod := M2; 
    // How to cast this? 
    ComponentMethod := M2; 
end; 

コンパイラが、私はそれを行うことはできません。

[dcc32 Error] Unit1.pas(32): E2010 Incompatible types: 'TComponent' and 'TForm' 

メソッドポインタを別の互換性のあるメソッドポインタにキャストする方法はありますか?

+0

これらは絶対的に***互換性がありません***(引用符でさえありません)。 'TForm'メンバを使わずに' M2'を実装した場合(これは非フォームで 'ComponentMethod'を呼び出すと壊れるため)、' TForm'入力を行うための 'M2'を宣言するポイントはありません。 –

+0

これは、はるかに複雑な問題の単純化された例です。私は、メソッドが正しい型(またはいくつかのサブタイプ)で呼び出されることを確認する必要があることを知っています。メソッドポインタのキャストは反変です。 –

+0

これと他のコメントはあなたの質問を[XY問題](http://xyproblem.info/)のように見せてくれます。実際の問題については、型固有のコールバックイベントを使用して解決するためのアイデアが出てきたので、私たちに語ったことはありません。一般的に格納されます。この考え方は、サポートされている型を拡張したい場合はいつでも、ボイラープレートの編集を恐れてしまうタイプキャストにつながり、単一のパラメータコールバックに対してのみ機能するという点でかなり制限的です。私はあなたが達成しようとしていることについてまだ明確ではありません。あなたはCommandオブジェクトを考慮したのだろうか? –

答えて

4

あなたがこれを行うことができます:

var 
    FormMethod: TFormMethod; 
    ComponentMethod: TComponentMethod; 
begin 
    FormMethod := M2; 
    ComponentMethod := TComponentMethod(FormMethod); 
end; 

を私の知る限り見ることができるように、トリックはあなたがその後ComponentMethodにそれを割り当てる前に、一時的なローカル変数に割り当てる必要があるということです。

ご存じのように、これは型保証されていません。 TFormから派生しない引数でComponentMethodが呼び出された場合、コンパイラーはユーザーを保存できません。

3

これらは互換性がありません。

M2が入力の1つとしてTFormを取る場合、コンパイラはフォームメソッド/メンバーがM2の本体でアクセスされることを合理的に期待できます。

TComponentMethodイベントハンドラは、それを呼び出すためにTComponentインスタンスが必要です。したがって、コンパイラが許可している場合は、TComponentインスタンスのTFormメンバーにアクセスできます。 ()明らかに災害のためのレシピ

例えば、言っ

procedure TForm1.M2(const AForm: TForm); 
begin 
    AForm.ModalResult := mrCancel; 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
var 
    ComponentMethod: TComponentMethod; 
begin 
    ComponentMethod := M2; 

    //The next line is legal. But if the previous line were legal, 
    //you'd attempt to access TForm(AComponent).ModalResult ... 
    //An AV if you're lucky, and weird behaviour if you're not. 
    ComponentMethod(TComponent.Create(Self)); 
end; 

は、あなたがハード型キャストを実行することができます。ただし、 TComponentMethod(M2)は、コンパイラが識別子を使用するたびにいつでも M2を呼び出すため動作しません。最初は FormMethod := M2が可能な「コンパイラマジック」のほんの一部です。したがって、 M2への参照を保持するためには、中間イベントハンドラ変数が必要です。イベントハンドラ変数は関数ではないので、呼び出すことなく型キャストすることができます。

Temp := M2; 
ComponentMethod := TComponentMethod(Temp); 

WARNINGこれはひどいアイデアですが、それは動作します:

  • リマインダーは次のようにComponentMethod変数を使用することができます。ComponentMethod(AComponentThatsNotAForm)procedure M2(const AComponent: TComponent);(それはその仕事をするために、フォームのインスタンスを必要としない場合は、ドン」:あなたはが本当に前の呼び出しが原因で実装されているかM2次のように、あなたがM2宣言しているはずで安全であることを保証できる場合
  • )... そして、足で自分を撃つ方法を見つけるために必死に努力する必要はありません。
+0

私はそれが意味を成すユースケースを持っています。私はTComponentMethodポインタだけでなく、元のTClassと "is"と ".InheritsFrom"を使ってテストを保存します。全体的な目的は、通常動作しないコンテナにさまざまなメソッドを格納することです。 –

-1

ダビッドの答えはいいです。私は、キャストするたびにソースタイプの一時変数を宣言しなくて済むように、汎用メソッドを書いています。

type 
    TOneArgMethod<T> = procedure(const A: T) of object; 


function TForm1.CastMethod<TFrom, TTo>(AFrom: TOneArgMethod<TFrom>) 
    : TOneArgMethod<TTo>; 
var 
    Method: TMethod; 
begin 
    Method := TMethod(AFrom); 
    Result := TOneArgMethod<TTo>(Method); 
end; 
+3

あなたは何故それほどひどいアイデアをしたいのですか?それほど頻繁に行うのではなく、簡単にやりたいヘルパーの方法が必要ですか?安全ではないタイプキャストを実行することで、コードを非常に壊れやすくするよりも、デザインを再検討する方がずっと良いマイレージが得られると強く思っています。 –

関連する問題