C# 委托与事件
内含 C# 委托与事件基础。
参考资料
- 三系联合暑培内部资料
- https://docs.microsoft.com/en-us/dotnet/csharp/
- https://www.runoob.com/csharp/csharp-tutorial.html
委托
委托(Delegates)是一种运行时绑定机制。
- 为什么使用委托?
对于任意晚绑定算法的支持、支持多播、事件模式
delegate
关键字
如何声明委托?
例如:
// From the .NET Core library
// Define the delegate type:
public delegate int Comparison<in T>(T left, T right);
这行语句在干什么?在定义类型!
delegate <return type> <delegate-name> <parameter list>
便会生成System.Delegate
的一个派生类Comparison
.
这个派生类自动带有了 add
和 remove
句柄.
创建委托类型的实例
// inside a class definition:
// Declare an instance of that type:
public Comparison<T> comparator;
调用委托类型的实例
int result = comparator(left, right);
如果委托没有目标怎么办?
throw NullReferenceException
如何调整委托的目标?
// static public void Call1() => Console.WriteLine("Call1");
// static public void Call2() => Console.WriteLine("Call2");
// static public void Call3() => Console.WriteLine("Call3");
var caller = new Action(Call1);
caller += Call2;
caller = caller + Call3;
caller -= Call1;
caller.Invoke(); // 等价于 caller();
如何调用委托?
private static int CompareLength(string left, string right) =>
left.Length.CompareTo(right.Length);
// phrases is List<string>
// 1
phrases.Sort(CompareLength); // Accepted
// 2
Comparison<string> comparer = CompareLength;
phrases.Sort(comparer); // Accepted
// 3: **lambda 表达式**
Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer); // Accepted
System.MulticastDelegate
类
要记住哪些方法?
Invoke()
, BeginInvoke()
, EndInvoke()
一些特殊类型(内置委托)
- Action
public delegate void Action();
public delegate void Action<in T>(T arg);
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
- Func
public delegate TResult Func<out TResult>();
public delegate TResult Func<in T1, out TResult>(T1 arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
// Other variations removed for brevity
例子
参见 https://docs.microsoft.com/en-us/dotnet/csharp/delegates-patterns
事件
事件也是一种晚绑定机制。
事件:允许对象broadcast事件的发生。
C# 中使用事件机制实现线程间的通信。
事件的设计理念?
建立 event source 和 event sink 之间的联系
subscribe to 和 unsubscribe from 一个事件 十分简单
单个event source支持多个subscribers.
通过事件使用委托
事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。
发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。
订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。
事件的声明
在类的内部声明事件,首先必须声明该事件的委托类型。
public delegate void BoilerLogHandler(string status);
然后我们基于委托类型定义事件:
// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;
上面的代码定义了一个名为 BoilerLogHandler 的委托和一个名为 BoilerEventLog 的事件.
一旦该事件被生成,该委托便会被调用.
示例
using System;
namespace SimpleEvent
{
using System;
/***********发布器类***********/
public class EventTest
{
private int value;
public delegate void NumManipulationHandler();
public event NumManipulationHandler ChangeNum;
protected virtual void OnNumChanged()
{
if ( ChangeNum != null )
{
ChangeNum(); /* 事件被触发 */
}else {
Console.WriteLine( "event not fire" );
Console.ReadKey(); /* 回车继续 */
}
}
public EventTest()
{
int n = 5;
SetValue( n );
}
public void SetValue( int n )
{
if ( value != n )
{
value = n;
OnNumChanged();
}
}
}
/***********订阅器类***********/
public class subscribEvent
{
public void printf()
{
Console.WriteLine( "event fire" );
Console.ReadKey(); /* 回车继续 */
}
}
/***********触发***********/
public class MainClass
{
public static void Main()
{
EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */
subscribEvent v = new subscribEvent(); /* 实例化对象 */
e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */
e.SetValue( 7 );
e.SetValue( 11 );
}
}
}
using System;
using System.IO;
namespace BoilerEventAppl
{
// boiler 类
class Boiler
{
private int temp;
private int pressure;
public Boiler(int t, int p)
{
temp = t;
pressure = p;
}
public int getTemp()
{
return temp;
}
public int getPressure()
{
return pressure;
}
}
// 事件发布器
class DelegateBoilerEvent
{
public delegate void BoilerLogHandler(string status);
// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;
public void LogProcess()
{
string remarks = "O. K";
Boiler b = new Boiler(100, 12);
int t = b.getTemp();
int p = b.getPressure();
if(t > 150 || t < 80 || p < 12 || p > 15)
{
remarks = "Need Maintenance";
}
OnBoilerEventLog("Logging Info:\n");
OnBoilerEventLog("Temparature " + t + "\nPressure: " + p);
OnBoilerEventLog("\nMessage: " + remarks);
}
protected void OnBoilerEventLog(string message)
{
if (BoilerEventLog != null)
{
BoilerEventLog(message);
}
}
}
// 该类保留写入日志文件的条款
class BoilerInfoLogger
{
FileStream fs;
StreamWriter sw;
public BoilerInfoLogger(string filename)
{
fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs);
}
public void Logger(string info)
{
sw.WriteLine(info);
}
public void Close()
{
sw.Close();
fs.Close();
}
}
// 事件订阅器
public class RecordBoilerInfo
{
static void Logger(string info)
{
Console.WriteLine(info);
}//end of Logger
static void Main(string[] args)
{
BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt");
DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent();
boilerEvent.BoilerEventLog += new
DelegateBoilerEvent.BoilerLogHandler(Logger);
boilerEvent.BoilerEventLog += new
DelegateBoilerEvent.BoilerLogHandler(filelog.Logger);
boilerEvent.LogProcess();
Console.ReadLine();
filelog.Close();
}//end of main
}//end of RecordBoilerInfo
}