C# 中委托与事件

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!");
		}
	}
}

编写事件函数时只需要找到事件委托的定义,根据定义写函数,添加到事件委托即可。

参考

blibli

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值