C# 中委托与事件
委托
定义&例子
委托:委托是一个对象,他知道如何调用一个方法
委托类型:定义了方法的返回类型和参数
所有委托都派生于System.MulticastDelegate,System.MulticastDelegate又派生于System.Delegate
namespace Demo{
public class Program{
delegate int Transformer(int x);
static int Square(int x)=>x*x;
static void Main(){
Transformer t = Square;
int res = t(x);
Console.WriteLine(res); // 9
}
}
}
编写插件式的方法,方法在运行时赋值给委托变量
namespace Demo{
public delegate int Transformer(int x);
public class Util{
public static void Transform(int[] values, Transformer T){
for(int i=0; i<values.length(); i++){
values[i] = T(values[i]);
}
}
}
public class test{
int[] values={1,2,3}
Util.Transform(values, Square);
foreach(int i in values){
Console.Write(i+" ");
}
static int Square(int x)=>x*x;
}
}
多播委托
- +和+=操作符可以合并委托实例
SomeDelegate d = someMethod1;
d += someMethod2;
- 委托调用的顺序与定义的顺序一致
- -和-=可以移除委托
- 多播委托返回值非void时,调用者从最后一个被调用的方法接收返回值。前面的方法仍然可用,只是返回值被丢弃了。
实例方法和静态方法
- 当一个实例对象赋值给委托对象时,委托对象不仅保留对实例方法的引用,同时保留对方法所属实例的引用。
- System.Delegate 的Target属性就是该实例,静态方法中Target=null
namespace Demo{
public delegate void ProgressReporter(int percentComplete);
class Test{
X x = new X();
ProgressReporter p = x.InstanceProgress;
p(99);
Console.WriteLine(p.Target==x);\\True
Console.WriteLine(p.Method);\\void InstanceProgress(int)
}
class X{
public void InstanceProgress(int percentComplete)==> Console.WriteLine(percentComplete);
}
}
泛型委托类型
// 委托类型可以包含泛型类型参数
public delegate T Transformer<T> (T arg);
Func 和 Action委托
//Func有返回类型
delegate TResult Func <out TResult>();
delegate TResult Func <in T, out TResult>(T arg);
delegate TResult Func <in T1, in T2, out TResult>(T1 arg, T2 arg);
//... and so on, up to T16
//Action有返回类型
delegate void Action();
delegate void Action<in T>(T arg);
delegate void Action<in T1, in T2>(T1 arg, T2 arg);
//... and so on, up to T16
委托和接口的区别
- 委托可以解决的问题,接口都可以解决
- 只能使用委托不能使用接口的情况:① 接口只能定义一个方法 ② 需要多播能力 ③ 订阅者需要 多次实现接口
事件
广播和订阅
- 使用委托的时候,通常会出现两个角色,一个广播者,一个订阅者
- 广播者这个类型包含一个委托字段,官博这通过调用委托来决定什么时候进行广播
- 订阅者是方法目标的接收者,订阅者可以决定何时开始或结束监听,方式是通过在委托上调用+=和-=
- 一个订阅者不知道和不干扰其他的订阅者
event 事件
- 事件就是将上述模式正式化的一个语言特性
- 事件是一种结构,为了实现广播者/订阅者模型,它只暴露了所需的委托特性的部分子集
- 事件的主要目的就是防止订阅者之间相互干扰。
event 声明
Broadcaster类里面的代码拥有对PriceChanged的完全访问权,在这里就可以把它当作委托。
而Broadcaster类型之外的代码只能对PriceChanged这个event执行+=或-=操作。
// 在委托前面加上event关键字
// 委托定义
public delegate void PriceChangedHandler(decimal oldprice. decimal newPrice);
public class Broadcaster{
// 事件声明
public event PriceChangedHandler PriceChanged;
}
标准的事件模式
为了编写事件,.NET定义了一个标准的模式,System.EventArgs,一个预定义的框架类,除了静态的Empty属性之外,没有其它成员。EventArgs是为事件传递信息的类的基类。
定义事件
//定义事件
public class PriceChangedEventArgs :System.EventArgs{
public readonly decimal lastPrice;\\通常通过属性或只读字段来暴露数据。
public readonly decimal NewPrice;
public PriceChangeEventArgs(decimal lastPrice, decimal newPrice){
LastPrice = lastPrice;
NewPrice = newPrice;
}
}
为事件选择或定义委托
委托定义的规则:①返回类型是void ② 接收两个参数,第一个参数类型是object,第二个参数类型是EventArgs的子类,第一个参数表示事件的广播者,第二个参数包含需要传递的信息; ③ 名称必须以EventHandler结尾
//Framework 定义了一个泛型事件委托System.EventHandler<T>,它满足上述规则。
public delegate void EventHandler<TEventArgs>(object source, TEventArgs e) where TEventArgs:EventArgs;
针对选择的委托定义事件
public class Stock{
public event EventHander<PriceChangedEventArgs> PriceChanged;
}
可触发事件的protected virtual方法
// 方法名必须和事件一致,前面再加上On,接收一个EventArgs参数
public class Stock{
public event EventHandler<PriceChangedEventArgs> PriceChanged;
protected virtual void OnPriceChanged(PriceChangedEventArgs e){
if(PriceChanged != null) PriceChanged(this, e);
}
}
完整的代码
public class PriceChangedEventArgs :System.EventArgs{
public readonly decimal lastPrice;\\通常通过属性或只读字段来暴露数据。
public readonly decimal NewPrice;
public PriceChangeEventArgs(decimal lastPrice, decimal newPrice){
LastPrice = lastPrice;
NewPrice = newPrice;
}
}
public class Stock{
string symbol;
decimal price;
public Stock(string symbol){
this.symbol = symbol;
}
public event EventHandler<PriceChangedEventArgs> PriceChanged;
protected virtual void OnPriceChanged(PriceChangedEventArgs e){
if(PriceChanged != null) PriceChanged(this, e);
}
public decimal Price{
get{ return price; }
set{
if(price == value) return;
decimal oldPrice = price;
price = value;
OnPriceChanged(new PriceChangedEventArgs(oldPrice, price));
}
}
}
class Test{
static void Main(){
Stock stocl = new Stock("MSFT");
stock.Price = 120;
stock.PriceChanged += stock_PriceChanged;
stock.Price = 135;
}
static void stock_PriceChaned(object sender, PriceChangedEventArgs e){
if((e.NewPrice - e.LastPrice)/e.LastPrice>0.1M){
Console.WriteLine("Alert, 10% stock price increase!");
}
}
}
编写事件函数时只需要找到事件委托的定义,根据定义写函数,添加到事件委托即可。