接触委托、事件等知识好长时间了,也反反复复看了很多资料,都是一来就给我讲委托的语法、用法,却没有告诉我到底什么是委托。要知道什么是委托,先从回调说起!
提示:该博文适合已经学习过委托、事件、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,这块知识终于理顺了,博客也整理的几个小时,希望对大家有用!