LoginSignup
17
21

More than 5 years have passed since last update.

.NET で独自の AOP を導入したら不便になってしまった話

Posted at

ロギングのコードを毎度書かなくてよいって素晴らしいじゃないですか?
それではアスペクト指向プログラミング (AOP) を開発に取り入れよう。と思い、AOP をプロジェクトに導入してしたら不便になってしまった話をします。

AOP の実装方法

.NET で AOP を実装する方法をいくつかご紹介します。

  • RealProxy クラスを使用して、独自に実装する方法
  • Unity Container (Unity) ライブラリを使用する方法
  • PostSharp ライブラリを使用する方法(商用)

可能な限りライブラリに依存しない方法で進めていくプロジェクトだったので、今回は RealProxy クラスを使って実装することにしました。
参考にした URL を記載しておきます。

.NETにおいてAOPを実現する透過プロキシ - マイクロソフト系技術情報 Wiki

アスペクト指向プログラミング - RealProxy クラスによるアスペクト指向プログラミング

UnityでAOPをしよう in C# for Visual Studio 2012

C#のAOPライブラリ(PostSharp) - Status Code 303 - See Other

出来上がったコードたち

載せるコードを短くしたかったので簡略化しています。実際に使用していたときはメソッドのパラメーターや戻り値などをログファイルに出力したり、ロギング以外のプロキシも復数実装していました。

ロギングプロキシ

public class LogProxy<T> : RealProxy
{
    private readonly T _decorated;
    private LogProxy(T decorated) : base(typeof(T))
    {
        _decorated = decorated;
    }

    public static T Create(T instanse)
    {
        return (T)new LogProxy<T>(instanse).GetTransparentProxy();
    }

    public override IMessage Invoke(IMessage msg)
    {
        var methodCall = msg as IMethodCallMessage;
        var methodInfo = methodCall.MethodBase as MethodInfo;

        Console.WriteLine($"Before executing '{methodCall.MethodName}'");
        var result = methodInfo.Invoke(_decorated, methodCall.InArgs);
        Console.WriteLine($"After executing '{methodCall.MethodName}' ");

        return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
    }
}

プロキシを使用可能にするクラス

public class TestModel : MarshalByRefObject
{
    public string MethodA(string param)
    {
        Console.WriteLine($"Testing {param}!");
        return "MethodA OK!";
    }

    public string MethodB(string param)
    {
        Console.WriteLine($"Testing {param}!");
        return "MethodB OK!";
    }
}

実際に使用しているクラス

static void Main(string[] args)
{
    var test = LogProxy<TestModel>.Create(new TestModel());
    test.MethodA("123");
    test.MethodB("ABC");
}

出力結果

https://gyazo.com/9bf2d4463c8baf179f85ae007ed00e51

開発が進むにつれて邪魔になってきた AOP

出来上がったコードだけを見ると自動でログを書いてくれてとても便利です。他にも便利なプロキシクラスをたくさん増やしていきました。
しかし、便利なものが増えていくにつれて問題が発生していきます。その中でも特に厄介だったのがこの部分になります。

デバッグがしづらくなってきた

デバッグ時にステップインしているとプロキシクラスに飛ばされてしまいます。
メソッド等に対してステップインするときは、そのメソッドの処理に入りたいはずです。プロキシに飛ばされるのは凄くストレスです。

https://gyazo.com/e21e8bb53666922c520e429005636e4c

ウォッチウィンドウなどでクラスの状態を見ようとしても、呼び出し元から中身を見ることができません。

https://gyazo.com/fd54ee7445e9a06498c4b5da6f4ed31b

完成したアプリケーション

コード上から AOP はほぼ消えました。そしてロギングは必要な時にそれぞれのクラスで実装するようになりました。残っているのは邪魔になってくる前にテストが完了してリリースしてしまった部分になります。残念ながら技術的負債になってしまいました。

どうすればよかったのか

まとめです。独自に作成した場合と、ライブラリを使用した場合の二つに分けました。上手く開発に成功していれば「こうするべき」というのを書きたかったのですが、今回は上手くいかなかったので気をつけるべき点だけ記載します。

RealProxy クラスを使用する場合

デバッグがしづらくなることを前提に開発を進めてください。それと MarshalByRefObject を使用しているため、パフォーマンスへの影響も考慮してください。
そういった色々な部分も考えながら開発を進めるのは面倒なので、個人的にはもう使いたくありません。

ライブラリを使用する場合

AOP のライブラリに限らず、ライブラリを使用する場合のメリット/デメリットを考慮した上で導入してください。RealProxy クラスがあまりにも微妙だったため、どちらかと言えばライブラリを使用する方法のほうが好きです。

17
21
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
21