c#委任とイベント


  • 依頼
  • 依頼は何か
  • 使い方を依頼
  • 依頼の下地原理
  • 依頼vs関数ポインタ
  • 依頼定義の戻り値は通常voidなのはなぜですか?

  • 事件
  • 事件は何ですか
  • 事件の使い方
  • 依頼vsイベント(推薦):
  • カスタムイベント
  • .Net Frameworkの符号化仕様
  • 依頼、イベントとObserver設計モード
  • どのようにして1人のお客様だけがイベントを購読できるようにしますか?


  • 依頼
    依頼は何ですか.
    依頼の本質は関数のアドレスであり,c++の関数ポインタに似ている.委任を使用すると,関数をパラメータとして別の関数で呼び出すことができる.委任を使用して、関数参照を委任オブジェクトにカプセル化します.次に、この委任オブジェクトを呼び出すと、コンパイル時にどの関数が呼び出されるかを知る必要がなく、委任オブジェクト内の関数参照が指す関数を実行できます.マルチキャスト依頼(依頼チェーン):+=継続的な登録方法により,複数のメソッドを一度に呼び出すことができる.
    メリット:
  • 呼び出し方法を変更することなく、ビジネス機能を継続的に追加できる.プログラム中の論理判断の分岐を大幅に減らす
  • 「呼び出し元」と「実現者」のデカップリングを実現
  • 依頼の使い方
  • 定義依頼、delegateキーワード使用
  • 声明依頼例
  • 依頼方法登録
  • 依頼された関数署名はバインドするメソッドの署名と一致しなければならない
  • +=登録の追加;-=登録解除直接用=最終登録のみの方法有効
  • 呼び出し依頼
  • ダイレクトコール
  •     class UseDelegate
        {
            //2.    
            InformInfoHandler myInform;
    
            public UseDelegate()
            {
                //3.    
                myInform = new InformInfoHandler(InformA);
                myInform += new InformInfoHandler(InformB);
            }
    
            public void SendInfo()
            {
                //4.    (    )
                myInform("I am cx");
            }
    
            private void InformA(string name)
            {
                Console.WriteLine("{0},Hello A", name);
            }
            private void InformB(string name)
            {
                Console.WriteLine("{0},Hello B", name);
            }
        }

    委託の基礎原理
    依頼は本質的に「クラス」であり、クラスを定義できる場所であり、依頼を定義することができる.(逆コンパイルソフトによる表示証明が可能で、依頼は[mscorlib]System.MulticastDelegateを継承した特殊クラス)
    3つのコアメソッド:BeginInvoke,EndInvoke,およびInvokeは、PostMessageを使用して通信したり、非同期操作したり、EndInvokeメソッドを使用して非同期操作の完了を待ったりするような依頼メソッドを封入します.多くの関数ポインタを格納できる呼び出しリストです(ただし、関数署名は委任タイプ署名と同じでなければなりません).このリストを呼び出すと、リスト内のすべてのポインタに対応する方法が順次実行されます.委任オブジェクトobjは、作成時にメソッドSayHiへのポインタを作成して_に保存するmethodPtr中;targetにはSayHiメソッドが存在するクラスのオブジェクトが保存されている(例えば、このコードをフォームのボタンのクリックメソッドに書くと、このとき_targetはSayHiメソッドが存在するフォームオブジェクトである);invocationListには、追加の2つのメソッドのポインタが保存されています.
    委任vs関数ポインタ
    関数ポインタは静的関数のみを指し、delegateは静的関数も非静的メンバー関数も参照できます.非静的メンバー関数を参照する場合、delegateはこの関数のエントリポインタの参照だけでなく、この関数を呼び出すクラスインスタンスの参照も保存します.次に、delegateは、関数ポインタと比較して、オブジェクト向け、タイプ安全、信頼性の高い管理対象(managed)オブジェクトです.つまり、runtimeはdelegateが有効な方法を指すことを保証し、delegateが無効なアドレスまたは境界を越えたアドレスを指す心配はありません.
    なぜ委任定義の戻り値は通常voidなのですか?
  • 依頼変数は複数のサブスクライバに登録可能であり、戻り値が定義されている場合、複数のサブスクライバのメソッドはいずれもパブリッシャに数値を返し、結果として次の戻り値が前の戻り値を上書きしてしまうため、実際には最後のメソッド呼び出しの戻り値しか得られない.
  • パブリッシャーとサブスクライバは緩やかに結合されており、パブリッシャーは誰がそのイベントを購読したのか、なぜ購読したのか、サブスクライバの戻り値はおろか、サブスクライバに戻る方法で値を返す必要はないことが多い.

  • ≪イベント|Events|ldap≫
    イベント
    イベントは、特定のクラスに基づいて、イベント駆動モデルの専用委任に使用されます.イベントの本質は依頼のパッケージであり、add_を外部に提供することである.EventName(対応+=)とremove_EventName(対応-=)アクセスでは、共通の委任変数をプライベート変数として定義できます.これにより、クラスのカプセル化の原則を満たすEventは、クラスの内部でpublicと宣言してもprotectedと宣言してもprivateになるように、委任タイプの変数をカプセル化します.クラスの外部では、イベントを宣言するときに使用するアクセスと同じ「+=」を登録し、「-=」をログアウトします.
    C#のイベント処理は実際には特殊な署名を持つdelegateであり、public delegate void MyEventHandler(object sender,MyEventArgs e);そのうちの2つのパラメータ、senderはイベント送信者を表し、eはイベントパラメータクラスである.MyEventArgsクラスはイベントに関するデータを含むために使用され、すべてのイベントパラメータクラスはSystemから必要である.EventArgsクラス派生.もちろん、あなたのイベントにパラメータが含まれていない場合は、Systemを直接使用することができます.EventArgsクラスをパラメータとして使用します.
    イベントの使い方
    イベントpublic event InformInfoHandler eveMyInfomを宣言します.
    依頼vsイベント(推奨):
    イベントは本質的に依頼のインスタンス依頼であり、クラスの外部で定義することができ、イベントはクラスの内部でのみ定義され、カプセル化されます.依頼は、誰が呼び出し関数なのかを手配するだけでなく、直接呼び出すこともできますが、イベントは直接呼び出すことができず、いくつかの操作でしかトリガーできません.イベントは他のタイプのサブスクリプションにのみ使用され、クライアントはイベントを直接トリガーすることはできません.
    カスタムイベント
  • delegateオブジェクトタイプを定義します.2つのパラメータがあります.1つ目のパラメータはイベント送信者オブジェクトで、2つ目のパラメータはイベントパラメータクラスオブジェクトです.
  • イベントパラメータクラスを定義する、これはSystem.EventArgsクラス派生.イベントにパラメータがない場合は、このステップは省略できます.
  • delegateオブジェクトと同じパラメータと戻り値タイプを持つイベント処理方法を定義します.
  • イベントオブジェクトをeventキーワードで定義し、delegateオブジェクトでもある.
  • +=オペレータでイベントキューにイベントを追加(-=オペレータでイベントを削除)
  • イベントをトリガする必要がある場所でdelegateを呼び出すようにイベントトリガ方法を書く.一般的に、この方法はprotectedアクセス制限である必要があります.すなわち、publicで呼び出すことはできませんが、クラスによって継承することができます.名前はOnEventNameです.一般的に「Onイベント名」
  • イベントトリガーメソッドを適切な場所で呼び出してイベントをトリガーする.
  • public class EventTest
    {
        //1.  delegate  
        public delegate void MyEventHandler(object sender, System.EventArgs e);
    
        //2.(       )  
        public class MyEventCls
        {
            //3.        ,  delegate               
            public void MyEventFunc(object sender, System.EventArgs e)
            {
                Console.WriteLine("My event is ok!");
            }
        }
    
        //4. event         
        private event MyEventHandler myevent;
    
        private MyEventCls myecls;
    
        public EventTest()
        {
            myecls = new MyEventCls();
            //5. +=            
            this.myevent += new MyEventHandler(myecls.MyEventFunc);
        }
    
        //6.   delegate          
        protected void OnMyEvent(System.EventArgs e)
        {
            if (myevent != null)
                myevent(this, e);
        }
        public void RaiseEvent()
        {
            EventArgs e = new EventArgs();
            //7.    
            OnMyEvent(e);
        }
    
        public static void Main()
        {
            EventTest et = new EventTest();
            et.RaiseEvent();
        }
    }

    .Net Frameworkの符号化仕様
  • 依頼タイプの名称はEventHandlerで終わるべきです.
  • 依頼されたプロトタイプ定義:1つのvoidが値を返し、2つの入力パラメータ:1つのObjectタイプ、1つのEventArgsタイプ(またはEventArgsから継承)を受け入れる.
  • イベントのネーミングはEventHandlerを取り除いた後の残りの部分を依頼する.
  • EventArgsから継承されるタイプはEventArgsで終わるべきである.

  • 委任、イベントおよびObserver設計モード
    もし私たちが高級な給湯器を持っているとしたら、私たちはそれに電気を通して、水温が95度を超えると:1、スピーカーは音声を出して、水の温度を教えます;2、液晶画面も水温の表示を変えて、水がもうすぐ沸くことを示します.給湯器は3つの部分から構成されています:給湯器、警報器、ディスプレイ.給湯器は水を沸かすだけで、警報も水温も表示できません.水が沸くと警報器から警報が出て、ディスプレイにヒントと水温が表示されます.
    namespace Delegate
    {
        //    
        public class Heater
        {
            private int temperature;
            public string type = "RealFire 001";       //         
            public string area = "China Xian";         //         
            //    
            public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);
            public event BoiledEventHandler Boiled; //    
    
            //   BoiledEventArgs ,   Observer       
            public class BoiledEventArgs : EventArgs
            {
                public readonly int temperature;
                public BoiledEventArgs(int temperature)
                {
                    this.temperature = temperature;
                }
            }
    
            //        Heater     ,                
            protected virtual void OnBoiled(BoiledEventArgs e)
            {
                if (Boiled != null)
                { //        
                    Boiled(this, e);  //            
                }
            }
    
            //   
            public void BoilWater()
            {
                for (int i = 0; i <= 100; i++)
                {
                    temperature = i;
                    if (temperature > 95)
                    {
                        //  BoiledEventArgs   
                        BoiledEventArgs e = new BoiledEventArgs(temperature);
                        OnBoiled(e);  //    OnBolied  
                    }
                }
            }
        }
    
        //    
        public class Alarm
        {
            public void MakeAlert(Object sender, Heater.BoiledEventArgs e)
            {
                Heater heater = (Heater)sender;     //         ?
                //   sender       
                Console.WriteLine("Alarm:{0} - {1}: ", heater.area, heater.type);
                Console.WriteLine("Alarm:    ,    {0}   :", e.temperature);
                Console.WriteLine();
            }
        }
    
        //    
        public class Display
        {
            public static void ShowMsg(Object sender, Heater.BoiledEventArgs e)
            {   //    
                Heater heater = (Heater)sender;
                Console.WriteLine("Display:{0} - {1}: ", heater.area, heater.type);
                Console.WriteLine("Display:     ,    :{0} 。", e.temperature);
                Console.WriteLine();
            }
        }
    
        class Program
        {
            static void Main()
            {
                Heater heater = new Heater();
                Alarm alarm = new Alarm();
    
                heater.Boiled += alarm.MakeAlert;   //    
                heater.Boiled += (new Alarm()).MakeAlert;      //         
                heater.Boiled += newHeater.BoiledEventHandler(alarm.MakeAlert);    //       
                heater.Boiled += Display.ShowMsg;       //      
    
                heater.BoilWater();   //  ,             
            }
        }
    }

    イベントの購読を1つだけ許可するにはどうすればいいですか?
    方法1:イベントをprivateとして宣言し、登録と登録解除の2つの方法を提供します.
    public class Publishser
    {
        //         
        private event GeneralEventHandler NumberChanged;// NumberChanged                ,       
        //     
        public void Register(GeneralEventHandler method)
        {
            NumberChanged = method;//“=”,  “+=”,                
        }
        //     
        public void UnRegister(GeneralEventHandler method)
        {
            NumberChanged -= method;//  method         ,       ,             。
        }
    
        public void DoSomething()
        {
            if (NumberChanged != null)
            {
                //     
                string rtn = NumberChanged();
                Console.WriteLine("Return: {0}", rtn);
            }
        }
    }

    方法2:C#には、依頼変数をカプセル化するイベントアクセス(Event Accessor)というものが提供されています.
    class Program
    {
        static void Main(string[] args)
        {
            Publishser pub = new Publishser();
            Subscriber1 sub1 = new Subscriber1();
            Subscriber2 sub2 = new Subscriber2();
    
            pub.NumberChanged -= sub1.OnNumberChanged;  //        
            pub.NumberChanged += sub2.OnNumberChanged;  //    sub2
            pub.NumberChanged += sub1.OnNumberChanged;  // sub1 sub2     
    
            pub.DoSomething();//     
        }
    }
    
    //     
    public delegate string GeneralEventHandler();
    //        
    public class Publishser
    {
        //         
        private GeneralEventHandler numberChanged;
        //         
        public event GeneralEventHandler NumberChanged
        {
            add
            {
                numberChanged = value;
            }
            remove
            {
                numberChanged -= value;
            }
        }
    
        public void DoSomething()
        {
            if (numberChanged != null)
            {
                numberChanged();//           
            }
        }
    }
    
    //        
    public class Subscriber1
    {
        public string OnNumberChanged()
        {
            Console.WriteLine("Subscriber1 Invoked!");
            return "Subscriber1";
        }
    }
    public class Subscriber2 {/*     ,  */}
    

    ref: https://www.cnblogs.com/sjqq/p/6917497.html