18

重複の可能性:
属性による C# ラップ メソッド

私はそのような機能を実現したいと思います:

[Atomic]
public void Foo()
{           
    /* foo logic */
}

ここ[Atomic]で、attribute は属性であり、関数ロジックをトランザクション スコープ内にラップします。

using(var scope = new TransactionScope())
{
    /* foo logic */
    scope.Complete();
}

そのような属性を記述する方法は?

私は前に基本的に同じ質問をしたことがあります.AOPを使用してこれを実行できることは知っていますが、純粋な.NETを使用してこれを書くのに役立つ最も単純な概念実証の実装または役立つ記事を探していることについては言及しませんでした.フレームワーク(ブラウジング関連の質問を読んだタイプRealProxyとタイプを使用していると思います)。MarshalByRefObject

この示されている例を正確に解決する必要があります。基本的なことのように思えますので、一から学びたいと思います。今のところ、安全で柔軟である必要はありません。

4

4 に答える 4

23

基本的なことのようです...

これは、概念を理解するのは簡単ですが、実装するのはまったく簡単ではない (多くの) ものの 1 つです。

Oded の回答によると、.NET の属性は何もしません。それらは、他のコード (または開発者) が後でそれらを見ることができるようにのみ存在します。勝手なコメントと思ってください。

それを念頭に置いて、このように属性を書くことができます

public class AtomicAttribute : Attribute { } 

ここで難しいのは、その属性をスキャンしてコードの動作を変更するコードを作成する必要があることです。

C# がコンパイル済み言語であり、.NET CLR の規則を考えると、これを行うには理論的に 3 つの方法があります。

  1. C# コンパイラにフックし、その属性を認識したときに別のコードを出力するようにします。
    これはいいことのように思えますが、今はまったく不可能です。将来的には Roslyn プロジェクトでこれが可能になるかもしれませんが、現時点ではできません。

  2. C# コンパイラが MSIL に変換した後に .NET アセンブリをスキャンする何かを記述し、MSIL を変更します。
    これは基本的にPostSharpが行うことです。MSIL のスキャンと書き換えは困難です。役立つMono.Cecilなどのライブラリがありますが、それでも非常に難しい問題です。また、デバッガなどに干渉する可能性があります。

  3. .NET プロファイリング API を使用して実行中のプログラムを監視し、その属性を持つ関数呼び出しを確認するたびに、それを他のラッパー関数にリダイレクトします。
    これはおそらく最も簡単なオプションですが (それでも非常に難しいですが)、プログラムをプロファイラーの下で実行する必要があるという欠点があります。これは開発用 PC では問題ないかもしれませんが、展開しようとすると大きな問題が発生します。また、このアプローチを使用すると、パフォーマンスが大幅に低下する可能性があります。

私の意見では、トランザクションをセットアップするラッパー関数を作成し、実際の作業を行うラムダを渡すことが最善の策です。このような:

public static class Ext 
{
    public static void Atomic(Action action) 
    {
        using(var scope = new TransactionScope()) 
        {
            action();
            scope.Commit();
        }
    }
}

.....

using static Ext; // as of VS2015

public void Foo()
{
    Atomic(() => {
        // foo logic
    }
}

これに対する高度なコンピューター サイエンス用語は、高階プログラミングです。

于 2013-01-13T20:18:58.767 に答える
10

属性はメタデータです - それだけです。

このようなメタデータを利用できるツールは多数ありますが、そのようなツールは属性を認識している必要があります。

PostSharp などの AOP ツールは、そのようなメタデータを読み取って、何をどこでコードに組み込むかを認識します。

要するに - を書くだけでは何もAtomicAttribute得られませ- AOP を達成するために、この属性を認識し、それに対して「何か」を行うツールを介してコンパイル済みアセンブリを渡す必要があります。

于 2013-01-13T19:36:01.500 に答える
4

それはまったく基本的なことではありません。メソッドに属性があるという理由だけで余分なコードが実行されることはないため、TransactionScopeコードを配置する場所がありません。

あなたがする必要があるのは、アプリケーションの起動時にリフレクションを使用して、アセンブリ内のすべてのクラスのすべてのメソッドを反復処理し、 でマークされているメソッドを見つけてAtomicAttributeから、そのオブジェクトの周りにカスタム プロキシを作成することです。次に、おそらく依存性注入フレームワークを使用して、実際の実装ではなく、プロキシを呼び出すように他のすべてのものを取得します。

ほとんどの AOP フレームワークは、ビルド時にこれを行います。たとえば、PostSharp は、VisualStudio がアセンブリをビルドした後に実行されます。アセンブリをスキャンし、プロキシと AOP インターセプターを含めるように IL コードを書き換えます。このようにして、アセンブリは実行時にすべて設定されますが、IL は最初に記述したものから変更されています。

于 2013-01-13T20:12:53.500 に答える
3

おそらく、IoC コンテナーを使用してすべてのオブジェクトを解決しますか? タイプのインターセプターを構成し、呼び出されたメソッドがその属性で装飾されているかどうかを確認できます。すべてのメソッド呼び出しでリフレクションを使用する必要がないように、その情報をキャッシュできます。

したがって、これを行うと:

var something = IoC.Resolve<ISomething>();

somethingあなたが実装したオブジェクトではなく、プロキシです。そのプロキシでは、メソッド呼び出しの前後で何でもできます。

于 2013-01-13T20:25:37.067 に答える