2011-07-27 12 views
3

注:この質問は新しい情報で更新されました。このテキストの下半分をご覧ください。 (元quesitonは、コンテキストのためにここに残されている。)基本クラスメソッドの属性を継承クラスに適用するにはどうすればよいですか?


それががオーバーライドされたメソッドで定義されていた場合、属性がまだ適用されているので、私は自分の属性を定義することができます任意の方法はありますか?

私は、メソッドに何らかの動作を挿入する属性を持っているが、そのメソッドが子クラスのいずれのケースでも呼び出されたときにその動作が適用されないので、さあ。

class BaseClass 
{ 
    [MyThing] 
    virtual void SomeMethod() 
    { 
     // Do something fancy because of the attribute. 
    } 
} 

class ChildClass 
{ 
    override void SomeMethod() 
    { 
     // Fancy stuff does happen here too... 
     base.SomeMethod(); 
    } 

    void AnotherMethod() 
    { 
     // ...but not here. And I'd like it to =(
     base.SomeMethod(); 
    } 
} 

属性がそうのように定義されています。

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 
public class MyThingAttribute : Attribute 

属性を持つメソッドを見つけるための現在のコードは以下の通りです:

var implementation = typeof(TheTypeWereCurrentlyInvestigating); 
var allMethods = (from m in implementation.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy) 
        let attribs = (TransactionAttribute[]) m.GetCustomAttributes(typeof (TransactionAttribute), true) 
        where attribs.Length > 0 
        select Tuple.Create(m, attribs.Length > 0 ? attribs[0] : null)).ToList(); 

私はその部分を書いていない、と私は、私がそれのすべての部分の100%であるとは言えません...しかし、私は、今のところ、私が関与するすべてのコードを管理していると仮定することができます。 (これはオープンソースプロジェクトなので、少なくとも自分のバージョンを作成し、プロジェクトオーナーにパッチを提出することができます...)

私はいくつかの他のケースも持っています。どのような方法であっても、基本クラスのメソッドを呼び出しますが、私がこのクラスを解決すれば、他のクラスをどのように動作させるかについてのアイディアを得ることができます。そうでなければ、私は彼らと一緒に戻ってきます。


UPDATE:

OK、私はCastle.Transactionsプロジェクトに座っていませんどのような動作し、何を参照するには、いくつかの非常に簡単なテストを作成しました。それは、私の元々の仮定が何のために働いているのか、何がしていないのかはやや分かりませんでした。

私がやったこと:
私は属性で飾ら一つの方法を持ってテストクラスを作成し、これは動作が正常に注入したことを確認しAssertメソッドを呼び出します(つまりトランザクションがあること)。その後、このテストクラスを継承するクラスをいくつか作成し、どのケースでもすべてが期待通りに動作することを確認しました。私が見つけた何


テストクラス上と子クラスのさまざまなメソッドから直接テストメソッドを呼び出すことで、私はしていませんどのような動作し、何について、次を発見しました:

 
Method called     Access modifiers  Does it work? 
*************     ****************  ************* 
SomeMethod() on base class* N/A     Yes 
OtherMethod() on child  neither    NO <-- headache! 
OtherMethod() on child  hiding (new)   No 
SomeMethod() on child   hiding (new)   No 
OtherMethod() on child  overrides   No 
OtherMethod() on child*  overrides   Yes 
SomeMethod() on child   overrides   Yes 

*と記されているものを除くすべてのケースにおいて、試験に適用された方法からbase.SomeMethod()が呼び出されました。最初のケースでは、同じメソッドが呼び出されましたが、子クラスが含まれていないため、テストから直接呼び出しました。 2番目のケース(*と表示されているもの)では、オーバーライドメソッドが呼び出されました。つまり、this.SomeMethod()です。これは、最後のケースと実際は同じです。私は余分な修飾子を使用していないので、その方法では呼び出しは単にSomeMethod()です。

は、私が欲しい:
は、それは私が本当に解決したい「頭痛」と記されたケースです。私が子クラスから呼び出しているにもかかわらず、基本クラスに動作を注入する方法。

私はこのパターンをリポジトリで使用しているので、基本クラスでは属性で修飾されたSave(T entity)メソッドが定義されています。現在、トランザクションオーケストレーションを取得するためにこのメソッドをオーバーライドする必要があります。これにより、戻り値の型を変更できなくなります。基本クラスではvoidですが、私の実装では代わりにError<T>にしたいと思います。これはオーバーライド時には不可能です。メソッドの名前を変更することで問題を解決できないため、私は迷っています。

+2

なぜ「新しい」で「オーバーライド」されていないのですか?それは仮想であり、ベースを呼び出すので、「オーバーライド」はより単純に思えるでしょう。 –

+0

@Marc:私はこの記事を書いた直後に、私が気づいたのは、私のケースのうちの1つを解決しましたが、両方を解決していませんでした。私の編集を見てください。オーバーライドするメソッドで発生する余分なもののため、私はいつも起こるとは限りません。残念ながら、AnotherMethodからオーバーライドされたメソッドを呼び出すことはできません。 –

+0

@Tomas Lycken、 AnotherMethod()? – musefan

答えて

0

私があなただったら私はあなたのデザインを変更しようと思います。 anotherMethodのクラスでオーバーライドされたときにAnotherMethod()のbase.SomeMethod()を呼び出すと、実際には臭いがでます。

BaseClass.SomeMethod()の関連部分を保護されたメソッドで除外することはできません。この新しいメソッドに属性を置き、BaseClass.SomeMethod()およびanotherMethod()で呼び出すことはできません。ChildClass.SomeMethod )はまだオーバーライドするメソッドを呼び出しますか?

+0

私はいくつかの情報で私のポストを更新しました。 –

0

これ以上は実行できません。その下に行くことはできません。その周りを移動する必要があります。

  • がコミット/ロールバック注入する既存の属性を適用することはできません:あなた自身がAnotherMethod()で例外をキャッチしているので、それをロールバックすることはない、私はそれらを理解して

    あなたの事情を考慮。

  • AnotherMethod()のコミット/ロールバック注入が必要です。

私はTransactionAttributeは(擬似コード)これを変形、try-catchブロックにメソッドの身体を包むされている疑いがある:

public void SomeMethod() { 
    transaction.Begin(); 
    try { 
     DoStuff(); 
     transaction.Commit(); 
    } 
    catch { 
     transaction.Rollback(); 
    } 
} 
:この(擬似コード、および非常に単純化された)のようなものに

public void SomeMethod() { 
    DoStuff(); 
} 

これを念頭に置いてTransactionAttributeAnotherMethod()に適用し、キャッチした例外を再スローすることができます。

[TransactionAttribute] 
public void AnotherMethod() { 
    try { 
     DoStuff(); 
    } 
    catch (Exception ex) { 
     //deal with exception 
     throw; 
    } 
} 

それが不可能な場合 - あなただけTransactionAttributeを注入する動作の一部をしたい場合など - そして、あなたはおそらく、あなたがそれを注入したい行動を注入新しいTransactionAttributeを行う必要があります。 1つの可能性は、try-catchブロックを探し、適切な場所にコミットとロールバックを配置することですが、現在のバージョンよりも扱いにくい可能性があります。

+0

私はいくつかの情報で自分の投稿を更新しました。 –

0

ここで暗闇でドッキリ、しかし...

私はトランザクションの振る舞いは、IoCコンテナによって注入されると仮定して、あなたがChildClassを解決するときには、プロキシを作成することによってこれを行うことしています。したがって、トランザクションコードは、プロキシ経由で\ ChildClass.SomeMethodの後に実行されます。あなたが見ている振る舞いはBaseClass.SomeMethodにコードインジェクションがないので、ChildClass.AnotherMethodから呼び出しても、プロキシコードのインジェクションは含まれていないと思っています。

この場合、組成パターンと注入を使用してBaseClassを問題を解決することができます。

コンテナを使用して以下のクラスを解決した場合、BaseClass.SomeMethodメソッドの適切なbefore \ afterトランザクションコードを持つプロキシのBaseClassが挿入されます。したがって、トランザクションの動作と優雅な例外処理を得ることができます。

あなたはBaseClass用交換AnotherChildClassを作るの問題を整理するために通常のOOメカニズムで遊んで、またはインターフェイスを使用、などなど

public class AnotherChildClass 
{ 
    private readonly BaseClass _bling; 

    public AnotherChildClass(BaseClass bling) 
    { 
     _bling = bling; 
    } 

    public void AnotherMethod() 
    { 
     try 
     { 
      _bling.SomeMethod(); 
     } 
     catch (Exception) 
     { 
      //Do nothing... 
     } 
    } 
} 

を例えば、ビットurghをすることができますが、

public class AnotherChildClass : BaseClass 
{ 
    private readonly BaseClass _bling; 

    public AnotherChildClass(BaseClass bling) 
    { 
     _bling = bling; 
    } 

    public override void SomeMethod() 
    { 
     _bling.SomeMethod(); 
    } 

    public void AnotherMethod() 
    { 
     try 
     { 
      _bling.SomeMethod(); 
     } 
     catch (Exception) 
     { 
      //Do nothing... 
     } 
    } 
} 

更新

私は推測している:あなたは、画像を取得します最新の調査から、新しく使用されたケースが、生成されたIoCコンテナプロキシがSomeMethodをオーバーライドしてコードを挿入するのをブロックしているので、動作していないことに注意してください。 Childクラスの派生クラスを作成して、new SomeMethodメソッドをオーバーライドしてみてください。これは、プロキシのブロック方法を示しています。

private class BaseClass 
    { 
     public virtual void SomeMethod(){} 
    } 

    private class ChildClass : BaseClass 
    { 
     public new void SomeMethod() //<- Declaring new method will block proxy 
     { 
      base.SomeMethod(); 
     } 
    } 

    private class ChildClassIocProxy : ChildClass 
    { 
     public override void SomeMethod() //<-- Not possible! 
     { 
      //Injected - before Tx 
      base.SomeMethod(); 
      //Injected - after Tx 
     } 
    } 
+0

私はポストをさらにいくつかの情報で更新しました。 –

+0

@Tomasが更新されました。 –

+0

OK、ええ、私が始めようとした理由のいくつかを明確にすることはできませんでした。しかし、実際には私の問題を解決するものではありません。私が実際に必要とするケースの唯一のコードサンプルで再度更新します。 (なぜなら、私の考えは私が思うほど良くない理由を学びたいからです:P) –

関連する問題