c#委任とイベント
17790 ワード
依頼
依頼は何ですか.
依頼の本質は関数のアドレスであり,c++の関数ポインタに似ている.委任を使用すると,関数をパラメータとして別の関数で呼び出すことができる.委任を使用して、関数参照を委任オブジェクトにカプセル化します.次に、この委任オブジェクトを呼び出すと、コンパイル時にどの関数が呼び出されるかを知る必要がなく、委任オブジェクト内の関数参照が指す関数を実行できます.マルチキャスト依頼(依頼チェーン):+=継続的な登録方法により,複数のメソッドを一度に呼び出すことができる.
メリット:
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イベント(推奨):
イベントは本質的に依頼のインスタンス依頼であり、クラスの外部で定義することができ、イベントはクラスの内部でのみ定義され、カプセル化されます.依頼は、誰が呼び出し関数なのかを手配するだけでなく、直接呼び出すこともできますが、イベントは直接呼び出すことができず、いくつかの操作でしかトリガーできません.イベントは他のタイプのサブスクリプションにのみ使用され、クライアントはイベントを直接トリガーすることはできません.
カスタムイベント
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の符号化仕様
委任、イベントおよび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