C#刘铁猛入门教程基础三(委托和事件)

C#委托与事件详解

一、委托

1.1前置知识

定义:

在 C# 中,委托(Delegate) 是一种引用类型,专门用于封装对方法的引用,是c和c++函数指针的升级版,它可以理解为 “方法的类型安全容器”—— 就像变量可以存储数据一样,委托可以存储方法,并在需要时调用这些方法,这里埋下了伏笔,不只是像函数指针一样只能指向一个函数的地址。

一切都是地址

变量是莫地址为起点的一段内存中所存储的值

函数是某地址为启动的内存中存储的一组机械语言指令

直接调用

通过函数名称,CPU直接获得函数所在地址并执行

间接调用

通过函数指针,CPU读取函数指针存储的值获得函数地址并执行

1.2系统定义的委托类型           

Action是 .NET 预定义的无返回值、无参数的委托类型

Func也是预制的委托有多种重载,这里使用的是两个参数一个返回值的重载

 internal class Program
 {
     static void Main(string[] args)

     {  
       Calculator calculator = new Calculator();
         //Action action是声明了一个委托类型,等号后面的内容是创建了一个Action的实例,实例是指向这个对象的方法的指针(下文中提到声明方式也不止一种)
         Action action = new Action(calculator.Report);
         //直接调用
         calculator.Report();
         //间接调用(下文中提到不止这一种调用方式)
         action.Invoke();
         //有两个参数设置委托,当输入Func<时就能看到重载类型提示
         //为什么是使用Func<>因为这个方法创建的时候使用的泛型,这里做特化
         Func<int, int, int> func = new Func<int, int, int>(calculator.Add);
         Func<int, int, int> func1 = new Func<int, int, int>(calculator.Subtract);
         int x = 100;
         int y = 200;
         int z = 0;
//使用委托调用add方法
         z = func(x, y);
         Console.WriteLine(z);
//使用委托调用减方法
         z=func1(x,y);
         Console.WriteLine(z);
         z=func1.Invoke(x,y);
         Console.WriteLine(z);
//委托调用的另外一种方法
         z=func.Invoke(x,y);
         Console.WriteLine(z);

     }
 }
 class Calculator
 {
     public void Report()
     {
         Console.WriteLine("This is a calculator class");
     }
     public int Add(int a, int b)
     {
         return a + b;
     }
     public int Subtract(int a, int b)
     {
         return a - b;
     }
 }  

1.3自定义的委托类型和两种赋值方式

委托的格式(实例见下面自定义委托)
  • 委托于所封装的方法必须“类型兼容”,即:返回值类型一致,参数列表必须完全一致(参数个数、参数类型、参数顺序)
  • 委托是一种类,但是声明方式不一致,更像是函数的声明方式,函数即:返回值  函数名 (参数列表);委托是:gelegate 返回值  函数名 (参数列表);总结就是添加了gelegate关键字,如果将gelegate替换成*就和c语言声明函数指针一样了
  • 委托既然是一种类,那么他可以放在名称空间体中,也可以放在类中,那么就是类成员了

委托的两种赋值方式

  1. 显式创建委托实例委托类型 变量名 = new 委托类型(方法名);(类似于类的实例化)
  2. 隐式转换(推荐)委托类型 变量名 = 方法名;(编译器自动帮你完成 new 的过程),类似于变量的定义(int a=1;)
namespace ltm2
{  //创建一个委托返回值是double类型,参数是两个double类型,委托名称是Calc
    public delegate double Calc(double x, double y);
    internal class Program
    {
        static void Main(string[] args)

        {
//计算类的实例化
            Calculator calculator = new Calculator();
//显式委托
            Calc calc1 = new Calc(calculator.Add);
//隐式委托
            Calc calc2 = calculator.Subtract;
//显式
            Calc calc3 = new Calc(calculator.Multiply);
//隐式
            Calc calc4 = calculator.Divide;
            //通过委托间接调用四个方法
            double a = 10;
            double b = 100;
            Console.WriteLine(calc1(a, b));
            Console.WriteLine(calc2(a, b));
            Console.WriteLine(calc3(a, b));
            Console.WriteLine(calc4(a, b));
        }
    }
    class Calculator
    {

        public double Add(double a, double b)
        {
            return a + b;
        }
        public double Subtract(double a, double b)
        {
            return a - b;
        }
        public double Multiply(double a, double b)
        {
            return a * b;
        }
        public double Divide(double a, double b)
        {

            return a / b;
        }
    }
}

二、委托的使用

2.1将方法当作参数传给另一个方法

2.1.1模板方法(工厂模式)

借用外部方法来产生结果

A函数的形式参数是一个委托,他的结果依赖于参数函数B的返回值,并且当增加输入的方法,A方法的结构无需改变,可以和多个方法适配。

2.1.2模板方法的使用

下文中定义了四个类,分别为盒子,产品,包装工厂,产品工厂;关系是产品工厂生产产品给到包装工厂,包装工厂包装成盒子。

为什么说使用了模板方法?

因为包装工厂的包装函数(WrapProduct)的形式参数是一个返回值为产品类的委托类型,产品工厂的生产产品函数返回值也是产品类,那么就能将产品工厂的函数作为实际参数传入包装工厂的包装函数中,而包装工厂的包装函数的结果由传入的产品而定,符合模板方法的定义

有什么好处?

当我们需要增加产品进行包装流程时,只需改动产品工厂类,增加函数即可

包装工厂的包装流程是固定的,即:获得产品工厂的产品,并赋值给Box的实例并且返回这个实例,这也就解释了为什么叫工厂模式

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Factory
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //实例化工厂类
            WrapFactory wrap = new WrapFactory();
            ProductFactory productFactory = new ProductFactory();
            //定义两个委托
            Func<Product> func1=new Func<Product>(productFactory.MakePizza);
            Func<Product> func2 = new Func<Product>(productFactory.MakeSalad);
            //两个装盒的产品
            Box box1 = wrap.WrapProduct(func1);
            Box box2 = wrap.WrapProduct(func2);

//既然形式参数是委托那么无需自己再使用类的声明方式声明两个委托并指向函数了,直接将函数名称传给委托(隐式),上面的定义两个委托和装盒产品是可替换的
   //Box box1 = wrap.WrapProduct(productFactory.MakePizza);
   //Box box2 = wrap.WrapProduct(productFactory.MakeSalad);
            //验证
            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();
            Product product = getProduct();
            box.Product = product;
            return box;
        }

    }
    class ProductFactory
    {
        public Product MakePizza()
        {
            Product Product = new Product();
            Product.Name = "Pizza";
            return Product;
        }
        public Product MakeSalad()
        {
            Product Product = new Product();
            Product.Name = "Salad";
            return Product;
        }
    }
    
}

2.1.3回调方法

c#使用委托来实现回调函数

补充1:回调方法的定义

例如:我们有两个类一个是主流程类,一个是按钮类,我们设置主流程类是调用方,按钮类是回调方。

调用方是指在内部实例化被调用方的类,并可能使用他的属性和函数,即:调用和被调是类的关系并非指是谁调用方法,回调方法指的是当被调用方执行完成了最后执行的由调用方提供的方法

调用方:定义回调方法,绑定事件。

被调方:定义委托,执行自身任务,完成后执行回调方法。

// 1. 被调用方:定义回调契约(委托)
public class Button
{
    public delegate void ClickEventHandler(string buttonName); // 定义委托类型(契约)
    public event ClickEventHandler? OnClick; // 公开一个注册点(事件)

    // 被调用方在检测到点击时,触发回调
    public void SimulateClick()
    {
        Console.WriteLine("按钮被物理点击了...");
        OnClick?.Invoke("MyButton"); // 关键:这里调用了调用方注册的方法
    }
}

// 2. 调用方:提供回调方法实现
public class Program
{
    public static void Main()
    {
        var button = new Button(); // 被调用方

        // 将方法注册为回调(满足委托契约)
        button.OnClick += HandleButtonClick; 

        button.SimulateClick(); // 启动流程
    }

    // 回调方法:由调用方实现,符合委托签名
    private static void HandleButtonClick(string name)
    {
        Console.WriteLine($"回调函数被调用:按钮 {name} 被点击了!");
    }
}
实例

我们创建了两个类和一个委托:

一个客户类:包含短信通知和电话通知两种不同的方法,参数都是两个

一个餐厅类:用户点餐方法,重点参数是委托notifyCallback

一个两个参数,返回值为void的委托

为什么要使用委托?

餐厅类中的函数不需要知道具体用什么方式通知(短信 / 电话),但是我们的形式参数是委托类型,当我调用函数PlaceOrder的时候,根据传入的函数名来绑定委托,并在PlaceOrder中使用委托变量来调用这个方法。

有什么优点?

当我要增加或者修改通知方式时,只需要和声明的委托类型相同格式,无需修改Restaurant中的代码(解耦)

using System;

// 1. 定义“联系方式”的格式(委托:规定回调函数的签名)
// 相当于“通知必须包含订单号和取餐码”
public delegate void NotifyDelegate(string orderId, string pickUpCode);

// 2. 餐厅的点餐流程(主函数):接收订单和“联系方式”(回调函数)
public class Restaurant
{
    // 点餐方法:参数包括订单号和回调函数(联系方式)
    public void PlaceOrder(string orderId, NotifyDelegate notifyCallback)
    {
        Console.WriteLine($"餐厅收到订单 {orderId},开始制作...");
        
        // 模拟制作过程(2秒)
        System.Threading.Thread.Sleep(2000);
        
        // 餐品做好了,通过“回调函数”通知用户(调用留的联系方式)
        string pickUpCode = "A123"; // 生成取餐码
        notifyCallback(orderId, pickUpCode); // 执行回调
    }
}

// 3. 你的“联系方式”(具体的回调函数)
public class Customer
{
    // 方式1:短信通知(符合NotifyDelegate签名)
    public static void SmsNotify(string orderId, string pickUpCode)
    {
        Console.WriteLine($"【短信通知】您的订单 {orderId} 已完成,取餐码:{pickUpCode}");
    }
    
    // 方式2:电话通知(符合NotifyDelegate签名)
    public static void CallNotify(string orderId, string pickUpCode)
    {
        Console.WriteLine($"【电话通知】您好,您的订单 {orderId} 已做好,取餐码是 {pickUpCode},请来取餐!");
    }
}

// 4. 实际点餐流程
class Program
{
    static void Main()
    {
        Restaurant restaurant = new Restaurant();
        string orderId = "ORD10086";
        
        // 情况1:选择“短信通知”作为回调函数
        Console.WriteLine("=== 选择短信通知 ===");
        restaurant.PlaceOrder(orderId, Customer.SmsNotify);
        
        // 情况2:选择“电话通知”作为回调函数
        Console.WriteLine("\n=== 选择电话通知 ===");
        restaurant.PlaceOrder(orderId, Customer.CallNotify);
    }
}

2.2多播委托和异步委托

多播委托(Multicast Delegate)

是C#中一种特殊的委托类型,允许一个委托实例引用多个方法。当调用这个委托时,所有被引用的方法会按照添加顺序依次执行。这种机制通过++=运算符实现方法的合并,通过--=运算符实现方法的移除。

Action action = Method1;
action += Method2; // 合并Method2
action -= Method1; // 移除Method1
限制规则

1.参数列表必须相同

原因​:委托的签名在声明时确定。合并时,编译器会检查所有方法的参数列表(参数类型、顺序和数量)是否与委托声明一致

delegate void MyDelegate(int x, string y);
// 有效合并:参数列表匹配的方法
MyDelegate d1 = MethodA; // MethodA需有(int, string)参数
MyDelegate d2 = MethodB; // MethodB需有(int, string)参数
MyDelegate combined = d1 + d2;

2.可以合并多个不同的方法

多播委托通过调用列表(Invocation List)管理多个方法。使用+=运算符时,新方法会被添加到列表末尾;调用委托时,列表中的方法按顺序执行

Action log = () => Console.Write("A");
log += () => Console.Write("B"); 
log(); // 输出 "AB"

 3.返回值限制

  • 返回类型应为void​:若委托有非void返回值(如Func<int>),调用多播委托时只能获得最后一个方法的返回值,之前方法的返回值会被忽略,这容易导致逻辑混淆,因此一般建议多播委托的返回类型声明为void

  • 避免输出参数​:输出参数(out参数)同样会被最后一个方法覆盖,因此多播委托不宜使用输出参数。

2.3异步委托

  • 每一个运行的程序是一个进程,而每个进程有一个或多个线程
  • 每个线程的运行互不相关,但是如果对同一个资源(例如:一个变量值)访问,处理不当就会造成争夺资源的冲突。
  • 异步调用的机理就是多线程。
  • 上文提到的调用委托的方式都是同步的

后面再做补充

三、事件

3.1定义

事件是类的成员,作用为使类或者对象具备通知能力,”对象O拥有一个事件E“,当事件E发送时,O有能力通知别的对象。事件本质上是委托的包装器,他的作用也是类同属性对于字段,在对外暴露的时候只暴露事件,避免谁都可以调用委托。

3.2使用

用于对象和类之间的信息传递

我们抽象人和手机类,手机在收到消息时可以响动,那么这个响动就是手机类里面的事件,人类就收到了通知,人收到通知拿起了手机查看是什么消息(人这个类就响应了事件),一般手机都会在锁屏界面可以查看是什么消息(这个消息的内容就是事件数据)

3.3事件模型

发生->响应模型5个部分:“手机响了我查看”:其中有4个部分容易看出,手机、响了、我、查看,还有一个隐含的订阅关系(我关心这个手机响没响)

发生响应模型5个动作:有一个事件,有类关心这个事件、这个事件发生了、关心这个事件的类被通知,被通知的类根据事件信息(事件参数、事件数据、通知)做出某种响应

图中展示了事件的五个组成部分的组合方式:星越多越重要

补充:整个事件结构虽然构成了一个触发的系统,但还需要一个触发的条件,例如:如果是点击事件还需点击这个动作,特别是手动触发事件时只考虑五个部分触发条件是容易忽略的

事件是委托的包装器,类似于属性对于字段的包装,而拥有事件的我们称为事件的拥有者,拥有响应函数的我们称为事件的响应者

四、事件的使用实例

4.1简单的事件实例

 internal class Program
 {
     static void Main(string[] args)

     {//timer是事件的拥有者或者说事件源
      Timer timer =new Timer();
         timer.Interval = 1000;
         //boy是事件的响应者
         Boy boy =new Boy();
         //Elapsed是事件,而boy.Action是事件处理器,timer.Elapsed += boy.Action;将事件和事件处理器绑定起来
         timer.Elapsed += boy.Action;
         timer.Elapsed+=Girl.Action;
         timer.Start();
         Console.ReadLine();
     }
 }
 class Boy
 {   //这里是输入boy.Action后自动生成的
     internal void Action(object sender, ElapsedEventArgs e)
     {
         Console.WriteLine("I am a boy");
     }
 }
 class Girl
 {
     internal static void Action(object sender, ElapsedEventArgs e)
     {
         Console.WriteLine("I am a girl");
     }
 }

4.2一星事件实例

事件的拥有者和订阅者分属于不同的对象

 internal class Program
 {
     static void Main(string[] args)

     {
         //form1事件的拥有者
      Form form1 = new Form();
         //controller事件的订阅者
      Controller controller = new Controller(form1);
         form1.ShowDialog();
     }
 }
 class Controller
 {
     private Form form;
     public Controller(Form form)
     {
         if(form != null)
         {
             this.form = form;
             //form.Click事件,绑定事件处理器和事件
             this.form.Click+=this.FormClick;
         }
             
     }
     //事件处理器
     private void FormClick(object sender, EventArgs e)
     {
        this.form.Text = "Hello World";
     }

4.3二星事件

一个对象订阅自己的事件,拥有者和响应者都是同一个类

 internal class Program
 {
     static void Main(string[] args)

     {
        myform mf = new myform();
         mf.Click += mf.MyClick;
         mf.ShowDialog();
 
     }
 }

 class myform : Form
 {
     internal void MyClick(object sender, EventArgs e)
     {
         this.Text = "hello";
     }
 }

4.4三星事件(最广泛)

事件的拥有者是事件响应者一个成员,响应者订阅这个事件,下面代码中,button类是form类的一个成员,而根据谁拥有响应函数,谁是事件的响应者,那么from类是事件的响应者。

为什么说最广泛?

因为事件就是为了界面开发准备的,在下文的WPF事件中就能体现,以点击事件为例,响应函数在Mainwindow中,那么就符合三星事件的这个关系。

 internal class Program
 {
     static void Main(string[] args)

     {//这里实例化了一个form类,这是事件的订阅者
        Myform form = new Myform();
         form.ShowDialog();
 
     }
 }

 class Myform : Form
 {
     private Button button1;
     private TextBox textBox1;
     public Myform()
     {
         this.textBox1 = new TextBox();
         //这里实例化了一个button类,这是事件的拥有者,虽然写在form类里但是这里是构造函数,当form类被实例化的时候,button类也会被实例化
         this.button1=new Button();
         this.Controls.Add(this.button1);
         this.Controls.Add(this.textBox1);
         //事件Click和事件处理器ButtonClicked关联
         this.button1.Click += this.ButtonClicked;
     }
     //事件处理器
     private void ButtonClicked(object sender, EventArgs e)
     {
         this.textBox1.Text = "Hello World!!!!!!!!!!!!!!!!!!!!!!";
     }

    
 }

4.5自定义事件

4.5.1完整声明

1.定义一个委托类型,这里要注意委托是类,应该是和拥有者和响应者同级

   public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);

2.事件的拥有者、事件、事件的触发

这里我们看事件的写法完全类似于属性与字段,而且可以看出事件与委托的关系,事件是委托的包装器事件主要对外提供“订阅”(+=)和“取消订阅”(-=)的接口,而其内部的“调用”或“触发”本质上是操作一个委托,这样做的目的就是只在事件的拥有者内部可以调用这个事件包装的委托,不让其被外部调用,在事件的拥有者这个类的外部只能订阅或者取消订阅

   //Customer是事件的拥有者
        public  class Customer
        {
            //声明一个委托字段
            private OrderEventHandler orderEventHandler;
            //声明一个事件
            public event OrderEventHandler Order
            {
                add { orderEventHandler += value; }
                remove { this.orderEventHandler -= value; }
            }
            public double Bill { get; set; }     
         
            public void think()
            {
                if(this.orderEventHandler != null)
                {
                    OrderEventArgs e= new OrderEventArgs() ;
                    e.DishName = "回锅肉";
                    e.Size = "smail";
                    //事件的触发
                    this.orderEventHandler(this, e);
                }
            }
        }

3.订阅事件

 customer.Order += waiter.Action;

下方是完整的示例:

public partial class MainWindow : Window
    {
        //是一个携带信息的类,要求必须继承EventArgs
       public class OrderEventArgs:EventArgs
        {
            public string DishName { get; set; }
            public string Size { get; set; }
        }
        //加EventHander就是说明是作为事件的订阅器,      
        //声明一个委托类型,这里只是说输入的参数是对象罢了     
        public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
     
        public MainWindow()
        {
            Customer customer = new Customer();
            Waiter waiter = new Waiter();
            //订阅事件
            customer.Order += waiter.Action;
            customer.think();
            

        }

        private void Customer_Order(Customer customer, OrderEventArgs e)
        {
            throw new NotImplementedException();
        }

        //Customer是事件的拥有者
        public  class Customer
        {
            //声明一个委托字段
            private OrderEventHandler orderEventHandler;
            //声明一个事件
            public event OrderEventHandler Order
            {
                add { orderEventHandler += value; }
                remove { this.orderEventHandler -= value; }
            }
            public double Bill { get; set; }     
         
            public void think()
            {
                if(this.orderEventHandler != null)
                {
                    OrderEventArgs e= new OrderEventArgs() ;
                    e.DishName = "回锅肉";
                    e.Size = "smail";
                    //事件的触发
                    this.orderEventHandler(this, e);
                }
            }
        }
        //事件的响应者
        public class Waiter
        {
            //事件处理器
            internal void Action(Customer customer, OrderEventArgs e)
            {
                Console.WriteLine("I will serve you the dish-{0}", e.DishName);
                double price = 10;
                switch (e.Size)
                {
                    case "smail":
                        price = price * 0.5;
                        break;
                    case "large":
                        price = price * 1.5;
                        break;
                    default:
                        break;

                }
                customer.Bill += price;
            }
        }

    }

4.5.2简略声明(语法糖,field-like:像字段)

使用简略声明的步骤:

  1. 定义一个委托
  2. 使用简要方式声明事件
  3. 当要调用事件绑定的响应函数时,直接可以用事件名称
namespace Event_try
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    /// 
    
    public partial class MainWindow : Window
    {
        //是一个携带信息的类,要求必须继承EventArgs
       public class OrderEventArgs:EventArgs
        {
            public string DishName { get; set; }
            public string Size { get; set; }
        }
        //加EventHander就是说明是作为事件的订阅器,      
        //步骤一:声明一个委托类型,这里只是说输入的参数是对象罢了     
        public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);
     
        public MainWindow()
        {
            Customer customer = new Customer();
            Waiter waiter = new Waiter();
            //订阅事件
            customer.Order += waiter.Action;
            customer.think();
            

        }

        private void Customer_Order(Customer customer, OrderEventArgs e)
        {
            throw new NotImplementedException();
        }

        //Customer是事件的拥有者
        public  class Customer
        {
            #region 完整声明
            //声明一个委托字段
            //private OrderEventHandler orderEventHandler;
            ////声明一个事件
            //public event OrderEventHandler Order
            //{
            //    add { orderEventHandler += value; }
            //    remove { this.orderEventHandler -= value; }
            //}
            #endregion
            #region 简要声明
            //步骤二:声明一个事件,这里很像字段的声明,aaa作为比较
            public event OrderEventHandler Order;
            public int aaa;
            #endregion
           
            public double Bill { get; set; }     
         
            public void think()
            {
                //这里也不一样,好像是用了事件的名称作为了字段使用,但是其实这是语法糖,是系统为我们添加了委托类型字段去判断并且调用方法
                if(this.Order != null)
                {
                    OrderEventArgs e= new OrderEventArgs() ;
                    e.DishName = "回锅肉";
                    e.Size = "smail";
                  //手动调用事件
                    this.Order(this, e);
                }
            }
        }
        //事件的响应者
        public class Waiter
        {
            //事件处理器
            internal void Action(Customer customer, OrderEventArgs e)
            {
                Console.WriteLine("I will serve you the dish-{0}", e.DishName);
                double price = 10;
                switch (e.Size)
                {
                    case "smail":
                        price = price * 0.5;
                        break;
                    case "large":
                        price = price * 1.5;
                        break;
                    default:
                        break;

                }
                customer.Bill += price;
            }
        }

    }
}

4.5.3命名规则

用于声明Foo事件的委托,一般命名为FooEventHandler,可以使用厂商提供的EventHander来取代我们自定义的委托类型,代码修改如下:

  • 去掉自定义委托使用EventHander
  • 修改绑定函数的参数列表
  • 将响应函数参数列表中父类对象转换成子类对象
namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Customer customer = new Customer();
            Waiter waiter = new Waiter();
            //订阅事件
            customer.Order += waiter.Action;
            customer.think();
            
        }
    }
    //是一个携带信息的类,要求必须继承EventArgs
    public class OrderEventArgs : EventArgs
    {
        public string DishName { get; set; }
        public string Size { get; set; }
    }
    //加EventHander就是说明是作为事件的订阅器,      
    //步骤一:声明一个委托类型,这里只是说输入的参数是对象罢了     
    //public delegate void OrderEventHandler(Customer customer, OrderEventArgs e);





    //Customer是事件的拥有者
    public class Customer
    {
        #region 完整声明
        //声明一个委托字段
        //private OrderEventHandler orderEventHandler;
        ////声明一个事件
        //public event OrderEventHandler Order
        //{
        //    add { orderEventHandler += value; }
        //    remove { this.orderEventHandler -= value; }
        //}
        #endregion
        #region 简要声明
        //步骤二:声明一个事件,这里很像字段的声明,aaa作为比较
        public event EventHandler Order;
        public int aaa;
        #endregion

        public double Bill { get; set; }

        public void think()
        {
            //这里也不一样,好像是用了事件的名称作为了字段使用,但是其实这是语法糖,是系统为我们添加了委托类型字段去判断并且调用方法
            if (this.Order != null)
            {
                OrderEventArgs e = new OrderEventArgs();
                e.DishName = "回锅肉";
                e.Size = "smail";
                //手动调用事件
                this.Order(this, e);
            }
        }
    }
    //事件的响应者
    public class Waiter
    {
        //事件处理器
        internal void Action(object customer, EventArgs e)
        {
            Customer c1=customer as Customer;
            OrderEventArgs e1 = e as OrderEventArgs;
            Console.WriteLine("I will serve you the dish-{0}", e1.DishName);
            double price = 10;
            switch (e1.Size)
            {
                case "smail":
                    price = price * 0.5;
                    break;
                case "large":
                    price = price * 1.5;
                    break;
                default:
                    break;

            }
            c1.Bill += price;
        }
    }

}

五、WPF中的事件

事件本就是为了页面开发做的设计

例子:要实现在页面上点击按钮,文本框中出现hello

5.1WPF方式订阅事件

当在xaml页面我们输入特征Click时,可以选择添加事件处理程序,会生成后台代码中的同名点击事件,在xaml页面中绑定事件,就无需使用传统的方式来绑定事件,这里的Click特征就起到了订阅事件的作用

ui:

<Grid>
        <StackPanel>
            <TextBox x:Name="text_box" Height="50"></TextBox>
            <Button x:Name="Button" Click="Button_click" Height="50"></Button>
        </StackPanel>
    </Grid>

后台文件:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
//这里就显示了这个事件需要输入的参数,一个是类(事件的拥有者),一个为携带的数据
        private void Button_click(object sender, RoutedEventArgs e)
        {
            this.text_box.Text = "hello";

        }
    }

5.2传统方式订阅事件

我们修改上文中的xaml代码,删除掉Click特征

在后台代码中使用传统方式来订阅事件

 public MainWindow()
        {
            InitializeComponent();
            //显示调用
            this.Button.Click+=new RoutedEventHandler(Button_click);
            //隐式调用
            this.Button.Click += Button_click ;
        }

        private void Button_click(object sender, RoutedEventArgs e)
        {
            this.text_box.Text = "hello";

        }

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值