理解什么是委托、事件、Lambad表达式,从回调说起!

       接触委托、事件等知识好长时间了,也反反复复看了很多资料,都是一来就给我讲委托的语法、用法,却没有告诉我到底什么是委托。要知道什么是委托,先从回调说起!

       提示:该博文适合已经学习过委托、事件、Lambad表达式,但学的很模糊的朋友学习,如果你从未学习过这些东西,请看完第一节“1.什么是回调?”之后,通过其他更基础的资料学习之后再继续往下学习。在学习过程中,希望搭建一个项目跟着做一遍,这样更好理解。如文中有错乱之处,望指正!

       Demo下载地址:http://download.csdn.net/detail/wb09100310/8495759

1.什么是回调?

       所谓回调,是指我在A类调用B类里的一个方法,在B类的这个方法执行到某个时刻或条件的时候,又返回来调用A类的方法。这是我对回调的理解,先来看一个列子就好理解了。

示例:

       创建一个控制台应用程序,命名为DelegateTest,然后添加一个Product类,结构如下图:

      

 

      我们先看一下Profram.cs和Product的代码,再来解释。

      Program.cs代码:

     

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

namespace DelegateTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Product product = new Product(){Name = "iPhone6",Price = 5288};//对象初始化语法
            product.GetInfo(product);
            Console.ReadKey();
        }

        public static void Output(Product product)
        {
            Console.Write("产品名称:");
            Console.WriteLine(product.Name);
            Console.Write("产品价格:");
            Console.WriteLine(product.Price);
        }
    }
}


      Product.cs代码:

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

namespace DelegateTest
{
    /// <summary>
    /// 产品类
    /// </summary>
    public class Product
    {
        /// <summary>
        /// 产品名字
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 产品价格
        /// </summary>
        public int Price { get; set; }

        public void GetInfo(Product product)
        {
            if (product != null)
            {
                Program.Output(product);
            }

        }
    }
}


       解释一下,是这样的。在Program的Main方法里,我们通过初始化语法实例化了一个Product类,然后调用Product类里的GetInfo方法得到产品信息,而GetInfo方法又反过来调用Program类里的Output方法输出产品信息,这就是回调。运行结果:

       

        这里我们没有用委托,就用我们很普通的方法实现了回调。然而,我们在实际开发过程中是Product类应该被放到实体层的,而GetInfo方法应该是在业务逻辑层的,也就是说,他们直接的命名空间是不同的。为了说明,这里我就把Product类放到Model层,我们创建一个类库,命名为Model,然后在Model里添加一个类Product,并把之前Product的类拷贝过来,如果你是直接从DelegateTest里直接把Product类文件复制过来,那请修改Product类的命名空间为Model,结构如图:

       

       这时候,我们发现Program的Main方法里找不到Product类了,添加对Model层的引用后可以了。但是GetInfo方法里找不到Program类了,那我们能不能在Model层反过来添加对DelegateTest的对应呢?不可以的,这时候就可以用委托实现上述回调功能。

      

 

2.委托

       怎么定义委托,怎么使用委托就不说了,网上有的是资料。这里是如何理解委托,直接看代码,然后再解释。

       Product类中添加委托:

        /// <summary>
        /// 定义一个委托
        /// </summary>
        /// <param name="product"></param>
        public delegate void OutputHandler(Product product);
        /// <summary>
        /// 定义委托成员变量
        /// </summary>
        private OutputHandler outputHandler;
        /// <summary>
        /// 注册委托方法
        /// </summary>
        /// <param name="handler"></param>
        public void RegHandler(OutputHandler handler)
        {
            outputHandler = handler;
        }

      并修改GetInfo方法为:

        /// <summary>
        /// 得到产品信息
        /// </summary>
        /// <param name="product"></param>
        public void GetInfo(Product product)
        {
            if (product != null && outputHandler!=null)
            {
                outputHandler(product);
            }

        }



 

       Program里的Main方法修改如下:

        static void Main(string[] args)
        {
            Product product = new Product(){Name = "iPhone6",Price = 5288};//对象初始化语法
            product.RegHandler(Output);//将Output方法注册到委托中
            product.GetInfo(product);
            Console.ReadKey();
        }

 

       运行结果同上,解释一下:

       Product:定义了一个委托,并定义了一个委托变量,由于我们的委托变量为私有的,所以我们还需要再定义一个注册方法到委托变量的方法RegHandler。在GetInfo中,我们修改后为通过委托变量来回调Program中注册到委托变量中的Output方法,这样就输出了产品信息。

       Program:红色部分,将Output方法注册到委托变量,这样在调用GetInfo方法的时候自然就能回调到Output方法了。

       到这里应该能够理解委托中回调是怎么回事了,也就基本上理解什么委托了,那么什么又是事件呢?

 

3.事件

       事件实际上就是简化了委托的写法,主要是简化了注册方法。还是一样,先看代码,然后再来解释。

       Product类中委托修改为:

        /// <summary>
        /// 定义一个委托
        /// </summary>
        /// <param name="product"></param>
        public delegate void OutputHandler(Product product);
        /// <summary>
        /// 定义事件
        /// </summary>
        public event OutputHandler outputHandler;
    删除了RegHandler方法
     Program里的Main方法修改如下:
       static void Main(string[] args)
        {
            Product product = new Product() { Name = "iPhone6", Price = 5288 };//对象初始化语法
            product.outputHandler += Output;//注册事件处理程序
            product.GetInfo(product);
            Console.ReadKey();
        }

 

 

 

         运行结果同上,加了event关键字之后称之为事件,在注册方法的时候用“+=”符号就行,可以添加多个方法。移除方法列表上的方法用“-+”符号。接下来我们再修改一下,以便让它看上去符合微软推荐的事件模式。

         先看一下EventArgs类

        

         

    // 摘要: 
    //System.EventArgs 是包含事件数据的类的基类。
    [Serializable]
    [ComVisible(true)]
    public class EventArgs
    {
        // 摘要: 
        // 表示没有事件数据的事件。
        public static readonly EventArgs Empty;

        // 摘要: 
        // 初始化 System.EventArgs 类的新实例。
        public EventArgs();
    }

         EventArgs表示一个不发任何自定义消息的事件。对于简单的事件来说,传递的参数是EventArgs实力,如果要自定义传递数据参数,需要重新定义一个类,并继承EventArgs,我们的事件要传递的Product类,所以定义应该如下:

        

    public class ProductEventArgs:EventArgs
    {
        public readonly Product product;

        public ProductEventArgs(Product p)
        {
            product = p;
        }
    }


        然后我们的委托修改一下,其他不变。

      

        /// <summary>
        /// 定义一个委托
        /// </summary>
        /// <param name="product"></param>
        public delegate void OutputHandler(object sender,ProductEventArgs e);


         是不是很像ASP.NET控件的事件方法,sender是触发事件的对象,e是要传递的事件参数。GetInfo方法修改如下,
       

        /// <summary>
        /// 得到产品信息
        /// </summary>
        /// <param name="product"></param>
        public void GetInfo(Product product)
        {
            if (product != null && outputHandler!=null)
            {
                outputHandler(this,new ProductEventArgs(product));
            }

        }


          如此,Program类的Output方法修改如下,其他不变:

    class Program
    {
        static void Main(string[] args)
        {
            Product product = new Product() { Name = "iPhone6", Price = 5288 };//对象初始化语法
            product.outputHandler += Output;//注册事件处理程序
            product.GetInfo(product);
            Console.ReadKey();
        }

        public static void Output(object sender,ProductEventArgs e)
        {
            Console.Write("产品名称:");
            Console.WriteLine(e.product.Name);
            Console.Write("产品价格:");
            Console.WriteLine(e.product.Price);
        }
    }

         上面只是依照微软的事件模式,效果和前面是一样的。

 

4.匿名方法

        思考一下我们发现,Output方法很少会被委托之外的任何程序调用,手工定义一个由委托对象调用的独立方法会显的很繁琐,于是C#提供了匿名方法,我们可以如下改下Program类,Output方法需要了,运行结果同上:


 

    class Program
    {
        static void Main(string[] args)
        {
            Product product = new Product() { Name = "iPhone6", Price = 5288 };//对象初始化语法
            //product.outputHandler += Output;//注册事件处理程序
            product.outputHandler += delegate(object sender, ProductEventArgs e)
            {
                Console.Write("产品名称:");
                Console.WriteLine(e.product.Name);
                Console.Write("产品价格:");
                Console.WriteLine(e.product.Price);
            };
            product.GetInfo(product);
            Console.ReadKey();
        }

    }


5.Lambad表达式

         Lambad表达式只是用更简单的方法来写匿名方法,先贴代码再说,为了容易理解,我们再在Product类里添加一个委托

        

        /// <summary>
        /// 定义一个委托
        /// </summary>
        /// <param name="product"></param>
        public delegate int CompareHandler(int p);

          如果价格大于5000返回1,否则返回0,Program类修改如下:

         

    class Program
    {
        static void Main(string[] args)
        {
            Product product = new Product() { Name = "iPhone6", Price = 5288 };//对象初始化语法
            product.GetInfo(product);
            Product.CompareHandler c = i => i > 5000 ? 1 : 0;
            int r = c(product.Price);
            Console.Write(r);
            Console.ReadKey();
        }

    }


        解释一下:i是参数列表,和定义一个方法参数列表一样,多个参数用“,”隔开,可以加上参数类型,=>符号后面的语句是处理参数列表的语句。那如果是输出之前的产品信息该怎么写呢?,Progarm修改如下:

       

class Program
    {
        static void Main(string[] args)
        {
            Product product = new Product() { Name = "iPhone6", Price = 5288 };//对象初始化语法
            //product.outputHandler += Output;//注册事件处理程序
            //product.outputHandler += delegate(object sender, ProductEventArgs e)
            //{
            //    Console.Write("产品名称:");
            //    Console.WriteLine(e.product.Name);
            //    Console.Write("产品价格:");
            //    Console.WriteLine(e.product.Price);
            //};
            //product.GetInfo(product);
            product.outputHandler += (object sender, ProductEventArgs e) =>
            {
                Console.Write("产品名称:");
                Console.WriteLine(e.product.Name);
                Console.Write("产品价格:");
                Console.WriteLine(e.product.Price);
            };
            product.GetInfo(product);
            Console.ReadKey();
        }

    }

    

        OK,这块知识终于理顺了,博客也整理的几个小时,希望对大家有用!

         
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值