アスペクト指向プログラミング:自分で学習して実行してください!

この記事は、いくつかのタスクに便利でシンプルなインターセプトメカニズムが必要であったという事実から生まれました。 実行時にILコードの動的な子クラスの実装を必要とするインターセプター(Casle.net、Spring.net、LinFuなど)が非常に多くあり、インターセプトされたクラスにほとんど常に同じ制限をもたらします。静的、非シール、メソッドおよびプロパティは仮想などである必要があります...



他の傍受メカニズムでは、アセンブリプロセスの変更またはライセンスの購入が必要でした。 私はどちらか一方を買う余裕がありませんでした...



はじめに



AOP-アスペクト指向プログラミング。 誰もがOPの意味に精通していると思うので、 アスペクトとは何かを理解する必要があります。 後の記事でこれについて心配する必要はありません。



この記事は初心者レベルで書こうと思います。 さらに読むための唯一の要件は、オブジェクト指向プログラミングの概念の知識です。



概念を理解することで、実装をよりよく理解できるように思えます。 しかし、退屈したり疲れたりした場合でも実装の部分を見るため、この記事が非常に大きいことも理解しています。 いつでも理論に戻ることができます。



経験豊富な開発者、読んでください!



すでにAOPに精通している場合は、離れないでください!

ここで私に聞かせて、今私はこの記事にあるものを明らかにします...



傍受する手法について説明します。

•すべてのクラス(シールド、静的、重要なタイプを含む)

•コンストラクター

•タイプ初期化子

•インスタンスメソッド、プロパティ、およびイベント(仮想としてマークされていない場合でも)

•静的メソッド、プロパティ、およびイベント



なし:

•コードとアセンブリを再描画します

•ILコードへの埋め込み

•動的に何かを作成する

•ターゲットクラスの変更

•ウィーバーが何かを実装する必要がある(たとえば、MarshalByRef)



一般的に、.Net 1.1で実行できるクリーンなマネージコードについて話します(一般的に、私は少しLinqを使用しますが、簡単にそれを取り除くことができます)。



最後に明確にします。 この手法を使用する:

•System.DateTime.NowやSystem.IO.File!などの構造をインターセプトできます!

•すべての一般的な傍受ライブラリに共通する通常の制限はありません。

まだ疑問がある? 次に読んでください!



AOPの原則



入門



オブジェクト指向プログラミングの学習方法はまだ完全ではないと考える人もいるかもしれませんが、OOPからAOPに切り替えて、長年のハードトレーニングで学習したすべてのプラクティスを放棄するのは理にかなっていますか? 答えは簡単です。切り替える必要はありません。 OOPとAOPの間に対立はありません! AOPは、私の意見では、その名前が誤解を招くような概念です。 AOPの原則を理解するには、OOPで使用するすべてを失うことはできないため、クラス、オブジェクト、継承、ポリモーフィズム、抽象化などを操作する必要があります。



昔は、インターネットがイエローページ、掲示板、ユーザーのページで構成されていたため、本を読んで何かを学ぶのが最善でした(新世代への注意:本は、テキストを含む縫い合わせた紙で構成されています)。 そして、これらの本のほとんどすべては、次のOOPルールを常に思い出させました。

•ルール番号1:データとコードのカプセル化が必要です

•ルール2:ルール1を破らない



カプセル化は、OOPが第3世代言語(3GL)で登場した主な目標でした。



ウィキから:

カプセル化は、類似しているが異なる2つの要件と、場合によってはそれらの組み合わせをサポートするために使用されます。

•オブジェクトの一部のコンポーネントへのアクセスを制限します。

•データを、そのデータで機能するメソッド(または他の機能)と組み合わせることを容易にする言語構成。





一般に、AOPは、このデータなしでカプセル化されたデータを操作するメソッド(またはその他の関数)の統合を促進するために、言語構造が必要になる場合があると主張しています。



キャッチしましたか? 実際、理論から必要なのはそれだけです。 いいね!



それでは、次の2つの新たな問題に進みましょう。

•いつ「時々」ですか?

•なぜこれが必要なのですか?



AOPの利点について...いくつかのシナリオ



シナリオA


あなたは銀行の開発者です。 銀行には素晴らしいシステムがあります。 ビジネスは安定しています。 政府は、銀行に透明性を高めることを要求する法令を発行しています。 銀行への、または銀行からの資金の移動がある場合、このアクションを記録する必要があります。 また、政府では、これは透明性に向けた最初の一歩に過ぎず、さらに多くのことがあると彼らは言います。



シナリオb


Webアプリケーションがテスターチームに発行されました。 すべての機能テストに合格し、アプリケーションは負荷テストを中断しました。 サーバー上で500ミリ秒を超えてページを処理できないという非機能要件があります。 分析後、結果をキャッシュすることで回避できる多数のデータベースクエリが見つかりました。



シナリオB


200年以上のクラスを含む理想的なライブラリで2年間ドメインモデルを構築してきました。 後で、アプリケーション用に新しいフロントエンドが作成されており、すべてのオブジェクトをUIに関連付ける必要があることがわかります。 ただし、問題を解決するには、すべてのクラスがINotifyPropertyChangedを実装する必要があります。



上記の例は、AOPが救いになる場所を示しています。 これらのシナリオはすべて、次の点で類似しています。



横断的関心事


「ときどき」それはいつですか?



一部のクラス(銀行システム、データアクセスサービス、ドメインモデル)は、一般に「彼らのビジネス」ではない機能を取得する必要があります。



•銀行システムのビジネスは、送金です。 伐採作業には政府が必要です。

•データを受信するには、データアクセスサービスが必要です。 データキャッシングは非機能要件です。

•ドメインモデルクラスは、会社のビジネスロジックを実装します。 プロパティの変更に関するUIの通知は、UIでのみ必要です。



一般に、これらのクラスに固有ではない問題を解決するために、さまざまなクラスのコードを記述する必要がある状況について話します。 AOPの方言で話す-実装されたアクションが必要です。



実装されているアクションを理解することがAOPの鍵です。 実装されたアクションはありません-AOPの必要はありません。



なぜこれが必要なのですか?

シナリオBを詳しく見てみましょう。



問題は、たとえば、各クラスに平均5つのプロパティがあることです。 200以上のクラスがあるため、次のようなコードを変換するには、1000以上のテンプレートコードを実装(コピーアンドペースト)する必要があります。

public class Customer { public string Name { get; set; } }
      
      





次のようなもので:

 public class Customer : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private string _Name; public string Name { get { return _Name; } set { if (value != _Name) { _Name = value; SignalPropertyChanged("Name"); } } } void SignalPropertyChanged(string propertyName) { var pcEvent = this.PropertyChanged; if (pcEvent != null) pcEvent(this, new PropertyChangedEventArgs(propertyName)); } }
      
      







うわー そして、これはただ一つの財産です! ところで、あなたは最新ですか? インターンは数日前に辞めました



「このデータなしでカプセル化されたデータを操作するメソッド(または他の関数)の組み合わせ」が必要です。 つまり、Customer型のドメインモデルのクラスを変更せずに、または最小限の変更でINotifyPropertyChangedアクションの実装を実装します。



これを実装できる場合、次のようになります。

•アクションの明確な分離

•コードの繰り返しを避け、開発をスピードアップする

•大量の定型コードの下でドメインクラスを隠さないようにします。



わあ しかし、これをすべて行う方法は?



理論的な答え


いくつかのクラスで実行する必要のあるアクションを実装しています(以降、目標と呼びます)。 実装(ロギング、キャッシュ、または他の何かを実装するコード)は、AOPアクションと呼ばれます。



次に、必要な場所にアクションを追加(埋め込み、埋め込み、単語の選択)する必要があります(これは重要です。アクションは実装されたアクションの実装です)。 そして、アクションの実装の目標の次の場所のいずれかを選択できるはずです。

•静的初期化子

•コンストラクター

•静的プロパティの読み取りと書き込み

•インスタンスプロパティの読み取りと書き込み

•静的メソッド

•インスタンスメソッド

•デストラクタ



理想的なAOPでは、ターゲットコードの任意の行にアクションを実装できる必要があります。

すばらしいですが、アクションをアタッチする必要がある場合、ターゲットにインターセプターが必要ですか? はい、CEP!



AOPでは、インターセプターの説明(アクションが実行される場所)の名前はpointcutです。 そして、コードが実際にバインドする場所:接続ポイント。



いいですか たぶんそうではありません...ここにちょっとした擬似コードがあります。

 //  class BankAccount { public string AccountNumber {get;} public int Balance {get; set;} void Withdraw(int AmountToWithdraw) { :public pointcut1; //  ( ,   -    ) Balance -= AmountToWithdraw; } } //   concern LoggingConcern { void LogWithdraw(int AmountToWithdraw) { //   ,     //  'this' –    BankAccount. Console.WriteLine(this.AccountNumber + " withdrawal on-going..."); } } class Program { void Main() { //        pointcut = typeof(Bank).GetPointcut("pointcut1"); //     LoggingConcern.Join(cutpoint, LogWithdraw); //        , //    LoggingConcern   pointcut1  } }
      
      





このようなメカニズムをそのままC#で使用するのはクールでしょうか?



さらにいくつかの概念


実装に移る前に、さらにいくつかの概念を示します...



アスペクトとは何ですか?

これは、アクション、スライス、および接続ポイントの組み合わせです。



少し考えてみてください。すべてが適切に機能することを願っています。アプリケーションの指定された場所(スライス)に、実行(接続ポイント)のためのロギングメソッドを登録するロギングメカニズム(アクション)があります。 これらはすべて、私のアプリケーションの側面です。



しかし、ちょっと待ってください...実装後、アクションは何をすべきであり、何ができますか?



アクションは2つのカテゴリに分類されます。



副作用。

副作用は、スライス内のコードのアクションを変更しないアクションです。 副作用として、実行するコマンドの種類を追加するだけです。

ロギングアクションは、副作用の良い例です。 環境がターゲットメソッド(Bank.Withdraw(int Amount)など)を実行すると、LoggingConcern.LogWithdraw(int Amount)メソッドが実行され、Bank.Withdraw(int Amount)メソッドの実行が継続されます。



ヒント

ヒント-メソッドの入力/出力を変更できるアクション。

アクションキャッシングは素晴らしい例です。 ターゲットメソッドが実行されると(たとえば、CustomerService.GetById(int Id))、CachingConcern.TryGetCustomerById(int Id)メソッドが実行され、キャッシュで見つかった値を返すか、存在しない場合は実行を継続します。



ヒントは次のとおりです。

•ターゲットセクションのパラメーターと、必要に応じて変更する機能を確認します。

•ターゲットメソッドの実行をキャンセルし、別の実装に置き換えます

•ターゲットメソッドの戻り値を確認し、変更または置換する



まだ読んでいますか? ブラボー! Parce que vous le valez bien ...



これで、AOPの概念と概念は終わりです。 C#で彼をもっとよく知りましょう。



私たちの実装



コードu je tue le chienを見せてください!


アクション


アクションは、このマジックを実装する必要があります。これは目標のタイプです。

問題ありません!

 public interface IConcern<T> { T This { get; } //  ,   ? }
      
      







スライス(ポイントカット)


コードの各行のスライスを取得する簡単な方法はありません。 しかし、1つずつ取得することができ、System.Reflection.MethodBaseクラスを使用すると非常に簡単です。 MSDNはそれについて冗長ではありません:メソッドとコンストラクターに関する情報を提供します。



私たちの間では、MethodBaseを使用してスライスリンクを取得することが、このタスクで最も強力なツールです。

.Netで宣言するほとんどすべてが最終的にメソッドになるため、コンストラクタ、メソッド、プロパティ、およびイベントのスライサーにアクセスできます...



自分で見てください:

 public class Customer { public event EventHandler<EventArgs> NameChanged; public string Name { get; private set; } public void ChangeName(string newName) { Name = newName; NameChanged(this, EventArgs.Empty); } } class Program { static void Main(string[] args) { var t = typeof(Customer); //  (  ) var pointcut1 = t.GetConstructor(new Type[] { }); //  ChangeName var pointcut2 = t.GetMethod("ChangeName"); //  Name var nameProperty = t.GetProperty("Name"); var pointcut3 = nameProperty.GetGetMethod(); var pointcut4 = nameProperty.GetSetMethod(); //     NameChanged var NameChangedEvent = t.GetEvent("NameChanged"); var pointcut5 = NameChangedEvent.GetRaiseMethod(); var pointcut6 = NameChangedEvent.GetAddMethod(); var pointcut7 = NameChangedEvent.GetRemoveMethod(); } }
      
      







ジョインポイント


接続用のコードの記述は本当に簡単です。 このコードを見てください:

 void Join(System.Reflection.MethodBase pointcutMethod, System.Reflection.MethodBase concernMethod);
      
      





レジストリのようなものに追加することができます。これは後で行います。そして、このようなコードを書き始めることができます!!!

 public class Customer { public string Name { get; set;} public void DoYourOwnBusiness() { System.Diagnostics.Trace.WriteLine(Name + "   "); } } public class LoggingConcern : IConcern<Customer> { public Customer This { get; set; } public void DoSomething() { System.Diagnostics.Trace.WriteLine(This.Name + "    "); This.DoYourOwnBusiness(); System.Diagnostics.Trace.WriteLine(This.Name + "    "); } } class Program { static void Main(string[] args)h { //    Customer.DoSomething(); var pointcut1 = typeof(Customer).GetMethod("DoSomething"); var concernMethod = typeof(LoggingConcern).GetMethod("DoSomething"); //   AOP.Registry.Join(pointcut1, concernMethod); } }
      
      





擬似コードから遠いですか? 私の意見では、実際には...

それでは、次は何ですか?



すべてをまとめると...


ここで問題と楽しみが同時に始まります!

しかし、簡単なことから始めましょう



レジストリ


レジストリは、接続ポイントの記録を保持します。 接続ポイントのシングルトンリストを取得します。 接合点は単純な構造です:

 public struct Joinpoint { internal MethodBase PointcutMethod; internal MethodBase ConcernMethod; private Joinpoint(MethodBase pointcutMethod, MethodBase concernMethod) { PointcutMethod = pointcutMethod; ConcernMethod = concernMethod; } //       public static Joinpoint Create(MethodBase pointcutMethod, MethodBase concernMethod) { return new Joinpoint (pointcutMethod, concernMethod); } }
      
      





特別なことは何もありません... IEquatableを実装する必要もありますが、コードを短くするために削除しました。



そして、レジストリ。 このクラスはAOPと呼ばれ、シングルトンです。 Registryという静的プロパティを介して、一意のインスタンスへのアクセスを提供します。

 public class AOP : List<Joinpoint> { static readonly AOP _registry; static AOP() { _registry = new AOP(); } private AOP() { } public static AOP Registry { get { return _registry; } } [MethodImpl(MethodImplOptions.Synchronized)] public void Join(MethodBase pointcutMethod, MethodBase concernMethod) { var joinPoint = Joinpoint.Create(pointcutMethod, concernMethod); if (!this.Contains(joinPoint)) this.Add(joinPoint); } }
      
      







AOPクラスを使用して、次の構成を記述できます。

 AOP.Registry.Join(pointcut, concernMethod);
      
      







ヒューストン、問題があります


ここで、私たちは何かをする必要がある明白で大きな問題に直面しています。 開発者がこのように書いた場合

 var customer = new Customer {Name="test"}; customer.DoYourOwnBusiness();
      
      





その後、レジストリにアクセスする理由はなく、LoggingConcern.DoSomething()メソッドは開始されません。



問題は、そのような呼び出しを傍受する簡単な方法を.Netが提供していないことです。

組み込みのメカニズムがないため、独自に作成する必要があります。 メカニズムの機能により、AOPの実装の機能が決まります。

この記事の目的は、考えられるすべての傍受手法について説明することではありません。 傍受方法は、AOP実装の重要な違いであることに注意してください。



SharpCraftersのWebサイト(PostSharpの所有者)は、2つの主なテクニックに関する明確な情報を提供しています。

•コンパイル時の埋め込み

•実行時の埋め込み



実装-プロキシ


一般的に、インターセプトには3つのオプションがあることは秘密ではありません。

•.netアセンブリを取得するための独自の言語とコンパイラを作成します。コンパイル時に、どこにでも何でも埋め込むことができます。

•実行時にアセンブリの動作を変更するソリューションを実装します。

•呼び出しをインターセプトするクライアントとターゲットプロキシの間に配置します。



上級者向けのメモ:デバッガーとプロファイラーAPIの使用は本番環境では実行できないため、意図的にデバッガーとプロファイラーAPIの可能性を考慮しません。



最も高度な注意事項:Raslyn APIで使用される最初と2番目のオプションのハイブリッドを実装できますが、私が知っているように、それはまだ行われているだけです。 bon entendeur ...



さらに、コードの行をカットする必要がない場合、最初の2つのオプションは複雑すぎます。



3番目のオプションに進みましょう。 プロキシに関する2つのニュースがあります。良い点と悪い点です。

悪い-実行中に、ターゲットをガスケットのインスタンスに置き換える必要があります。 コンストラクターをインターセプトする場合は、ターゲットクラスのインスタンスの作成をファクトリーに委任する必要があります;この実装はアクションを統合できません。 ターゲットクラスのインスタンスが存在する場合、明示的に置換を要求する必要があります。 管理反転依存性注入のマスターの場合-これはタスクでもありません。 残りについては、これはファクトリーを使用してインターセプト手法のすべての機能を提供する必要があることを意味します。 でも心配しないで、この工場を建てます。



良いニュースは、プロキシを実装するために何もする必要がないということです。 System.Runtime.Remoting.Proxies.RealProxyクラスは、最適な方法で構築します。

私の意見では、クラスの名前はその目的を反映していません。 このクラスはプロキシではなく、インターセプターです。 それでも、彼はGetTransparentProxy()メソッドを呼び出してプロキシを作成しますが、これが実際に彼から必要なものです。



これが迎撃魚です。

 public class Interceptor : RealProxy, IRemotingTypeInfo { object theTarget { get; set; } public Interceptor(object target) : base(typeof(MarshalByRefObject)) { theTarget = target; } public override System.Runtime.Remoting.Messaging.IMessage Invoke(System.Runtime.Remoting.Messaging.IMessage msg) { IMethodCallMessage methodMessage = (IMethodCallMessage) msg; MethodBase method = methodMessage.MethodBase; object[] arguments = methodMessage.Args; object returnValue = null; // TODO: //     ,  AOP.Registry //     MethodBase,   "method"... //      ,     //   "theTarget"   ... ;-) return new ReturnMessage(returnValue, methodMessage.Args, methodMessage.ArgCount, methodMessage.LogicalCallContext, methodMessage); } #region IRemotingTypeInfo public string TypeName { get; set; } public bool CanCastTo(Type fromType, object o) { return true; } #endregion }
      
      





いくつかの説明、 私たちは実現の中心に登りました...



RealProxyクラスは、リモートオブジェクトからの呼び出しをインターセプトし、ターゲットを整理するように設計されています。 リモートは、真にリモートとして理解する必要があります:別のアプリケーション、別のアプリケーションドメイン、別のサーバーなどからのオブジェクト。 詳細には触れませんが、.Netインフラストラクチャでリモートオブジェクトを整理するには、参照と値の2つの方法があります。 したがって、削除されたオブジェクトは、MarshalByRefを継承するか、ISerializableを実装する場合にのみ注文できます。 リモートオブジェクトの機能は使用しませんが、それでも、ターゲットがリモートコントロールをサポートしていると考えるにはRealProxyクラスが必要です。 このため、typeof(MarshalByRef)をRealProxyコンストラクターに渡します。



RealProxyは、Invokeメソッド(System.Runtime.Remoting.Messaging.IMessage msg)を使用して、透過プロキシ経由ですべての呼び出しを受信します。 メソッドの置換の本質を理解するのはここです。 上記のコードのコメントを参照してください。



IRemotingTypeInfoの実装に関して:実際のリモート環境では、クライアントはサーバーにオブジェクトを要求します。 クライアントアプリケーションは、受信するオブジェクトの種類について何も知らない場合があります。 したがって、クライアントアプリケーションがパブリックオブジェクトGetTransparentProxy()を呼び出すと、環境は返されたオブジェクト(透過プロキシ)がアプリケーションコントラクトに準拠することができます。 IRemotingTypeInfoを実装することにより、どのキャストが有効で、どのキャストが無効であるかをクライアント環境に示唆します。



ここで使用しているトリックに驚嘆してください。

 public bool CanCastTo(Type fromType, object o) { return true; }
      
      





AOPの実装全体は、これら2つの単語をリモートオブジェクトに書き込むことができるという理由だけで可能です。trueを返します。 これは、GetTransparentProxy()によって返されたオブジェクトを、環境チェックなしで任意のインターフェイスにキャストできることを意味します!!!!



水曜日は私たちにあらゆる行動の先取りを与えるだけです!

このコードを修正して、どのタイプでもtrueよりも合理的なものを返すことができます...しかし、 存在しないメソッドによって提供される動作を利用する方法や、インターフェイス全体をインターセプトする方法を想像することもできます...一般的に、想像のためのスペースがかなりあります...



これで、ターゲットインスタンスに適切なインターセプトメカニズムが既にあります。 しかし、コンストラクターのインターセプトと透過的なプロキシはまだありません。 これは工場にとっての挑戦です...



工場


何も言うことはありません。 これがクラスの魚です。

 public static class Factory { public static object Create<T>(params object[] constructorArgs) { T target; // TODO: //   typeof(T)   constructorArgs (-   ) //    ,         //   ,    -     //     “target” ()   //   GetProxy return GetProxyFor<T>(target); } public static object GetProxyFor<T>(object target = null) { //         // (    ,  ) //        return new Interceptor(target).GetTransparentProxy(); } }
      
      





Factoryクラスは常にオブジェクト型のオブジェクトを返すことに注意してください。 透過プロキシがT型ではなく、System.Runtime.Remoting.Proxies .__ TransparentProxy型であるため、T型のオブジェクトを返すことはできません。 しかし、環境から与えられた解像度を思い出してください。 返されたオブジェクトをチェックせずに任意のインターフェイスにキャストできます。



お客様に適切なコードを渡すことを期待して、AOPクラスにFactoryクラスを配置します。 これは、使用法セクションで確認できます。



最新の実装ノート


ここまで読んだら、あなたはヒーローです! ブラビシモ! 称賛!



この記事を簡潔で理解しやすいものにするために(そして何が面白いのか)、メソッドの取得と切り替えの実装の詳細についての退屈な議論はしません。 これについては何も興味深いことはありません。 しかし、まだ興味がある場合は、コードをダウンロードして確認してください-完全に機能しています! クラスとメソッドの名前は、わずかに異なる場合があります。 私はそれを並行して裁定しましたが、それほど大きな変更はないはずです。



注意! プロジェクトでコードを使用する前に、 paenultimusを注意深く読んでください 。 また、paenultimusの意味がわからない場合は、リンクをクリックしてください。




使用する


私はすでに多くのことを書きましたが、まだこれらすべてをどうするかを示していません。 そして、ここで彼は真実の瞬間です!



アーカイブの内容( ダウンロード


アーカイブには、次の側面の実装を示す5つの例が含まれています。

•デザイナーの傍受

•メソッドとプロパティの傍受

•イベントの傍受

•タイプ初期化インターセプト

•Interception File.ReadAllText(文字列パス)



そして、ここでは、5つのうちの2つを示します。



メソッドとプロパティの傍受


まず、ドメインモデルが必要です。 特別なことは何もありません。

 public interface IActor { string Name { get; set; } void Act(); } public class Actor : IActor { public string Name { get; set; } public void Act() { Console.WriteLine("My name is '{0}'. I am such a good actor!", Name); } }
      
      







今、私たちは行動が必要です

 public class TheConcern : IConcern<Actor> { public Actor This { get; set; } public string Name { set { This.Name = value + ". Hi, " + value + " you've been hacked"; } } public void Act() { This.Act(); Console.WriteLine("You think so...!"); } }
      
      







アプリケーションが初期化されると、接続ポイントについてレジストリに通知します

 // Weave the Name property setter AOP.Registry.Join ( typeof(Actor).GetProperty("Name").GetSetMethod(), typeof(TheConcern).GetProperty("Name").GetSetMethod() ); // Weave the Act method AOP.Registry.Join ( typeof(Actor).GetMethod("Act"), typeof(TheConcern).GetMethod("Act") );
      
      







そして最後に、ファクトリーでオブジェクトを作成します

 var actor1 = (IActor) AOP.Factory.Create<Actor>(); actor1.Name = "the Dude"; actor1.Act();
      
      





Actorクラスの作成をリクエストしましたが、結果をインターフェイスに持ってくることができることに注意してください。したがって、IActorにつながります。 クラスはそれを実装します。



これらすべてをコンソールアプリケーションで実行すると、次のようになります。

My name is 'the Dude. Hi, the Dude you've been hacked'. I am such a good actor!

You think so...!









Interception File.ReadAllText(文字列パス)


ここには2つの小さな問題があります。

•ファイルクラスは静的です

•インターフェイスを実装していません



「良い」を覚えていますか? 環境は、返されたプロキシのタイプとインターフェースをチェックしません。

したがって、任意のインターフェイスを作成できます。 目標も行動も誰も実現しません。 一般的に、インターフェイスはコントラクトとして使用します。

静的クラスFileを装ったインターフェイスを作成しましょう。

 public interface IFile { string[] ReadAllLines(string path); }
      
      







私たちの行動

 public class TheConcern { public static string[] ReadAllLines(string path) { return File.ReadAllLines(path).Select(x => x + " hacked...").ToArray(); } }
      
      







参加ポイント登録

 AOP.Registry.Join ( typeof(File).GetMethods().Where(x => x.Name == "ReadAllLines" && x.GetParameters().Count() == 1).First(), typeof(TheConcern).GetMethod("ReadAllLines") );
      
      







そして最後に、プログラムの実行

 var path = Path.Combine(Environment.CurrentDirectory, "Examples", "data.txt"); var file = (IFile) AOP.Factory.Create(typeof(File)); foreach (string s in file.ReadAllLines(path)) Console.WriteLine(s);
      
      







この例では、Factory.Createメソッドを使用できないことに注意してください。 静的型は引数として使用できません。



参照資料



特別な注文なし。

Qi4jのチュートリアル

RealProxyの拡張

動的プロキシチュートリアル(Castle.net)

LinFu.DynamicProxy:軽量プロキシジェネレーター

Dynamic Decoratorを使用してアスペクトを追加する

AOPインターセプターとして機能するCLRプロファイラーの実装

.Netの傍受

AspectF Fluent Way to Addspects to Aspects Cleaner Maintainable Code



次は?



AOPの主な目標であるアスペクトを実現し、実行のために登録することができました。 TinyAOPが誕生しました。 しかし、AOPの土地でのあなたの道はまだ終わっていないので、もっと深くしたいと思うかもしれません。



理由1:現在のように誰がジャンクションポイントを登録したいのですか? 間違いなく私のためではありません! 少しの分析をより実用的に行うことができ、実際のAOPライブラリに似ています。 AOPは、頭痛を引き起こすのではなく、人生を簡素化するために必要です。



理由2:不純物凝集トピックはまったく公開されておらず、それらから多くの利点が期待できます。



理由3:パフォーマンスと安定性が必要です。これで、コードは概念実証に過ぎません。非常に遅く、非常に高速にすることができます。エラーチェックでも問題はありません。



理由4:ほとんどすべてのクラスをインターセプトしますが、インターフェースのインターセプトはどうですか?



理由5:まだ少し理由がありますか?



おわりに



埋め込みなどを行わずにクリーンなマネージコードでAOPを実装する技術的な実現可能性を実証する、優れたコンパクトなプロトタイプがあります。



AOPについて理解したので、独自の実装を作成できます。



次に、翻訳者が紹介され、ギャグを運び始めます



このセクションについては著者に完全に同意しますが。

habahabr.ru、codeproject.comなどのようなサイトは、異なる人々が記事を投稿しているという理由だけで存在します。なぜ彼らがそれをするかは問題ではありませんが、この作業は行われています。著者を無視しないでください。記事が気に入らなかった場合は、コメントで説明してください。マイナスの場合、誰もより良い方法を知りません。気に入ったら、黙ってはいけません!



そして今、確かに翻訳者のギャグ



:AOPが機能しなかったから、実は、私の驚きの正気に私はそのためのコメントは、以下の条件のために良いと簡単なロシア語の単語提案でいることを提案し、多くの単語を翻訳

•アスペクトを(まだ理解できるようだが、)

•懸念

•ジョインポイント

•クロスカット

•ウィービング( weaver-何もありませんが、ロシア語を見つけた方が良いでしょう)



著者:Guirec Le Bars



All Articles