少なくとも 1 つの可能な解決策があります。それは、アスペクト指向アプローチと依存性注入を組み合わせることです。残念ながら、このアプローチは特定の種類のシナリオに限定されています(回答の最後のセクションを確認してください)。
注入されたアスペクトのアドバイス
まず、対象メソッドを含むクラスのオプションの依存関係someValue
に定義します。この依存関係を持つクラスからインターフェイスを抽出することでこれを行うことができます
public interface IServiceWithDependency
{
int SomeValue { get; set; }
}
次に、このインターフェースを実装するターゲットクラスを作成します
public class MyClass : IServiceWithDependency
{
// dependency
public int SomeValue { get; set; }
// aspect
[InjectedAspect]
public void DoSomething()
{
string otherValue = "456" + SomeValue;
}
}
ここで、この依存関係を注入するためのアスペクトが必要です
[Serializable]
public class InjectedAspectAttribute : MethodInterceptionAspect
{
public override void OnInvoke(MethodInterceptionArgs args)
{
if (args.Instance is IServiceWithDependency)
{
// inject actual value of dependency
(args.Instance as IServiceWithDependency).SomeValue = 123;
}
args.Proceed();
}
}
必要に応じて、コンパイル時の検証を追加して、この側面が実装するクラスにのみ適用されることを確認できます。IServiceWithDependency
[Serializable]
public class InjectedAspectAttribute : MethodInterceptionAspect
{
public override bool CompileTimeValidate(MethodBase method)
{
var result = true;
var methodInfo = method as MethodInfo;
if (!typeof(IServiceWithDependency).IsAssignableFrom(method.DeclaringType))
{
Message.Write(methodInfo, SeverityType.Error, "999", string.Format("Only class derived from IServiceWithDependencyallowed, {0} not implements IServiceWithDependency", method.DeclaringType));
result = false;
}
return result;
}
public override void OnInvoke(MethodInterceptionArgs args)
{
/* … */
}
}
完全なサンプルはこちらから入手できます
いつ適用するか、この側面はどのようなシナリオですか
最初のアイデアは、トランザクション管理の側面から生まれました。アンビエントがない場合TransactionScope
、代わりに、トランザクションを処理する必要があるすべてのクラスにトランザクション オブジェクトが注入されます。いくつかの重要な詳細:
- この側面はスレッドセーフではないだけでなく、クラスの 1 つのインスタンスでこの側面で装飾されたいくつかのメソッドを呼び出すと、以前に注入された値をオーバーライドする可能性があります。
- MVC アプリケーションにコントローラーのようなクラスがあり、クラスのインスタンスごとに 1 つのメソッドのみが呼び出される場合、以前の制限は問題になりません。
- また、これは 1 つのクラスのスコープ内のある種のコード臭グローバル変数であるため、このアプローチは本当に役立つ場合にのみ慎重に適用する必要があります。