c# 中 委托与事件
1.委托
是函数指针的升级版 也是类的一种
我们以两个比较常用的简单委托为示例:
Action(无返回值) 委托 与 Func(有返回值) 委托
namespace weituo_Demo{
class Program { static void Main(string[] args) { Calculate calculate = new Calculate(); Action action = new Action(calculate.Report); calculate.Report(); action.Invoke(); action(); Func<int, int, int> func1 = new Func<int, int, int>(calculate.Add); Func<int, int, int> func2 = new Func<int, int, int>(calculate.Sub); int x = 100; int y = 200; int z = 0; z = func1.Invoke(x, y); Console.WriteLine(z); z = func2.Invoke(x, y); Console.WriteLine(z); } } class Calculate { public void Report() { Console.WriteLine("I have 3 methods"); } public int Add(int a ,int b) { int result =a+b; return result; } public int Sub(int a ,int b) { int result = a - b; return result; } }
}
自定义委托类型:
namespace weituo_demo2{
public delegate double Calc(double x, double y);
class Program { static void Main(string[] args) { Calculator calcluator = new Calculator(); Calc calc1 = new Calc(calcluator.Add); Calc calc2 = new Calc(calcluator.Sub); Calc calc3 = new Calc(calcluator.Mul); Calc calc4 = new Calc(calcluator.Div); double a = 100; double b = 200; double c = 0; c = calc1.Invoke(a, b); Console.WriteLine(c); c = calc2.Invoke(a, b); Console.WriteLine(c); c = calc3.Invoke(a, b); Console.WriteLine(c); c = calc4.Invoke(a, b); Console.WriteLine(c); } } class Calculator { public double Add(double x,double y) { return x + y; } public double Sub(double x, double y) { return x - y; } public double Mul(double x,double y) { return x * y; } public double Div(double x,double y) { return x / y; } }
}
在自定义委托时要与所封装的方法保证类型兼容 {
返回值的数据类型一致
参数列表在个数和数据类型上一致
}
委托的一般使用 :
实例: 把方法当作参数传给另一个方法
a. 模板方法: “借用 ”指定的外部方法来产生结果
相当于“填空题 ”
常位于代码中部
委托有返回值
b. 回掉方法: 调用指定的外部方法
相当于“流水线”
常位于代码末尾
委托无返回值
a.模板方法:
namespace weituo_Demo3{
class Program { static void Main(string[] args) { ProductFactory productFactory = new ProductFactory(); WrapFactory wrapFactory = new WrapFactory(); Func<Product> func1 = new Func<Product>(productFactory.MakePizza); Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar); Box box1 = wrapFactory.WrapProduct(func1); Box box2 = wrapFactory.WrapProduct(func2); Console.WriteLine(box1.Product.Name); Console.WriteLine(box2.Product.Name); } } class Product { public string Name { get; set; } } class Box { public Product Product { get; set; } } class WrapFactory { public Box WrapProduct(Func<Product> getProduct) // 模板方法 { Box box = new Box(); box.Product = getProduct.Invoke(); return box; } } class ProductFactory { public Product MakePizza() { Product product = new Product(); product.Name = "Pizza"; return product; } public Product MakeToyCar() { Product product = new Product(); product.Name = "Toy Car"; return product; } }
}
b.回调方法
namespace weituo_Demo3{
class Program { static void Main(string[] args) { ProductFactory productFactory = new ProductFactory(); WrapFactory wrapFactory = new WrapFactory(); Func<Product> func1 = new Func<Product>(productFactory.MakePizza); Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar); Logger logger = new Logger(); Action<Product> log = new Action<Product>(logger.Log); Box box1 = wrapFactory.WrapProduct(func1,log); Box box2 = wrapFactory.WrapProduct(func2,log); Console.WriteLine(box1.Product.Name); Console.WriteLine(box2.Product.Name); } } class Logger { public void Log(Product product) { Console.WriteLine("Product'{0}' created at{1}. Price is{2}.",product.Name,DateTime.UtcNow,product.Price); } } class Product { public string Name { get; set; } public double Price { get; set; } } class Box { public Product Product { get; set; } } class WrapFactory { public Box WrapProduct(Func<Product> getProduct,Action<Product> logCallback) // 模板方法 { Box box = new Box(); box.Product = getProduct.Invoke(); if(box.Product.Price>= 50) { logCallback(box.Product); } return box; } } class ProductFactory { public Product MakePizza() { Product product = new Product(); product.Name = "Pizza"; product.Price = 12; return product; } public Product MakeToyCar() { Product product = new Product(); product.Name = "Toy Car"; product.Price = 100; return product; } }
}
无论是模板方法还是回调方法都是利用委托类型的参数封装一个外部的方法,然后把方法传进方法的内部,再进行间接调用;这就是对委托的常规用法。
委托的高级使用:
多播委托
action1 += action2;
action1 += action3;
action1.Invoke();
隐式异步调用
action1.BeginInvoke(null,null);
action2.BeginInvoke(null,null);
action3.BeginInvoke(null,null);
显示异步调用
Task task1 = new Task(new Action(stu.DoHomework));
task1.Start();
2.事件
a.事件的拥有者(event source,对象)
b.事件成员(event ,对象)
c.事件的响应者(event subscriber, 对象)
d.事件处理器(event handler,成员)---本质上是一个回调方法
e.事件订阅-----把事件处理器与事件关联在一起,本质上市一种以委托类型为基础的“约定”
注意: 事件处理器是方法成员
挂接事件处理器的时候,可以使用委托实例,也可以直接使用方法名,这是个语法糖
事件处理器对事件的订阅不是随意的,匹配与否由声明事件时所使用的委托类型来检查
事件可以同步调用也可以异步调用
eg:
1.namespace Event_Demo{
class Program { static void Main(string[] args) { Timer timer = new Timer(); timer.Interval = 1000; Boy boy = new Boy(); timer.Elapsed += boy.Action; timer.Start(); Console.ReadLine(); } } class Boy { internal void Action(object sender, ElapsedEventArgs e) { Console.WriteLine("Jump"); } }
}
2.
namespace Event_Demo{
class Program { static void Main(string[] args) { Form form = new Form(); //事件拥有者 Controller controller = new Controller(form); //事件相应者 form.ShowDialog();
} } class Controller { private Form form; public Controller (Form form) { if(form != null) { this.form = form; this.form.Click += this.FormClicked; } } private void FormClicked(object sender, EventArgs e) { this.form.Text = DateTime.Now.ToString(); } }
}
3.
namespace Event_Demo{
class Program { static void Main(string[] args) { MyForm form = new MyForm(); //事件拥有者,事件响应者 form.Click += form.FormClicked; form.ShowDialog(); } } class MyForm : Form { internal void FormClicked(object sender, EventArgs e) { this.Text = DateTime.Now.ToString(); } }
}
4.namespace Event_Demo{
class Program { static void Main(string[] args) { MyForm form = new MyForm(); //事件响应者 form.ShowDialog(); } } class MyForm : Form { private TextBox textbox; private Button button; //事件拥有者 public MyForm() { this.textbox = new TextBox(); this.button = new Button(); this.Controls.Add(textbox); this.Controls.Add(button); this.button.Click += this.ButtonClicked; } private void ButtonClicked(object sender, EventArgs e) { this.textbox.Text = "Hello World!"; } }
}
事件是基于委托的:
1.事件需要委托类型作为约束,这个约束既规定了事件能够发送什么消息给事件的响应者,也规定了事件的响应者能收到什么消息。这就决定了事件响应者的事件处理器必须能跟这个事件约束匹配上 它才能够订阅这个事件
2.当事件的相应者向事件的拥有者提供了能够匹配这个事件的事件处理器之后,总的找个地方把这个事件保存或者记录下来,能够记录或者引用方法的任务 只有委托才能做到 所以又要用到委托;
总结一下 : 事件无论从表层约束来讲或者从底层实现来讲都是依赖委托类型 。
委托是事件的底层基础 ,事件是委托的上层建筑。
(声明委托时 ,如果委托是为了某一个事件准本的 委托名必须以EventHandler 作为后缀 用来传递事件消息的类 以 EventArgs 做后缀)
事件的参数 sender objiect 类型 实际上就是事件的拥有这 source
EventArgs派生类 参数名为e 也就是事件参数
触发事件的访问级别一般为protected 如果为 public 就会变成"借刀杀人"
事件完整声明
namespace Event_Demo{
class Program { static void Main(string[] args) { Customer customer = new Customer(); Waiter waiter = new Waiter(); customer.order += waiter.Action; customer.Action(); customer.PayTheBill();
} } public class OrderEventArgs:EventArgs { public string DishName { get; set; } public string Size { get; set; } } public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
public class Customer
{ private OrderEventHandler orderEventHandler; public event OrderEventHandler order { add { this.orderEventHandler += value; } remove { this.orderEventHandler -= value; } } public double Bill { get; set; } public void PayTheBill() { Console.WriteLine(" I will pay ${0}.",this.Bill); } public void WalkIn() { Console.WriteLine("Walk into the restaurant"); } public void SitDown() { Console.WriteLine("Sit Down"); } public void Think() { for(int i = 0; i<5; i++) { Console.WriteLine("Let me think..."); Thread.Sleep(1000); } if(this.orderEventHandler != null) { OrderEventArgs e = new OrderEventArgs(); e.DishName = "Kongpao Chicken"; e.Size = "Large"; this.orderEventHandler.Invoke(this, e); } } public void Action() { Console.ReadLine(); this.WalkIn(); this.SitDown(); this.Think(); } } public class Waiter { public void Action(Customer customer, OrderEventArgs e) { Console.WriteLine("I will serve you the dish-{0}.",e.DishName); double price = 10; switch(e.Size){ case "Small": price = price * 0.5; break; case "Large": price = price * 1.5; break; default: break; } customer.Bill += price; } }
}
事件简化声明:(字段式声明)
namespace Event_Demo{
class Program { static void Main(string[] args) { Customer customer = new Customer(); Waiter waiter = new Waiter(); customer.Order += waiter.Action; customer.Action(); customer.PayTheBill();
} } public class OrderEventArgs:EventArgs { public string DishName { get; set; } public string Size { get; set; } } public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
public class Customer
{ public event OrderEventHandler Order; public double Bill { get; set; } public void PayTheBill() { Console.WriteLine(" I will pay ${0}.",this.Bill); } public void WalkIn() { Console.WriteLine("Walk into the restaurant"); } public void SitDown() { Console.WriteLine("Sit Down"); } public void Think() { for(int i = 0; i<5; i++) { Console.WriteLine("Let me think..."); Thread.Sleep(1000); } if(this.Order != null) { OrderEventArgs e = new OrderEventArgs(); e.DishName = "Kongpao Chicken"; e.Size = "Large"; this.Order.Invoke(this, e); } } public void Action() { Console.ReadLine(); this.WalkIn(); this.SitDown(); this.Think(); } } public class Waiter { public void Action(Customer customer, OrderEventArgs e) { Console.WriteLine("I will serve you the dish-{0}.",e.DishName); double price = 10; switch(e.Size){ case "Small": price = price * 0.5; break; case "Large": price = price * 1.5; break; default: break; } customer.Bill += price; } }
}
所以事件的本质是委托字段的一个包装器
这个包装器对委托字段的访问起限制作用,相当于一个“蒙板”
封装的一个重要功能就是隐藏
事件对外界隐藏了委托实例的大部分功能,仅暴露 "添加/移除事件处理器的功能"