自我C#进阶-委托、lambda表达式与事件
委托
委托的具体作用
大体上,在创建委托时和类是平级的,故可以将委托看成是一种特殊的类。和类相似,类里面可以定义方法,委托也可以绑定方法。不同的是类里面的方法会写出具体的实现,而委托只是将类进行绑定,委托所绑定的方法的具体实现是在其它类中。
故委托的作用可以理解为绑定某方法,在启用委托的同时,方法会执行。
委托的构建方法
通用语法:(后面会说到其他两种构建方式)
(修饰符)如public + (关键字)delegate + (返回类型)如void + 自定义委托名((参数)如string);
解释:
1.修饰符代表委托的作用域
2.关键字delegate表明构建的是一个委托,是必须的
3.返回类型代表通过委托绑定的方法执行后的返回类型(方法中定义的返回类型和委托中定义的返回类型必须相同)
4.参数代表绑定方法的参数类型(方法中定义的参数类型和委托中定义的参数类型必须相同)
简单委托的使用
具体采用一个例子来说明
//定义一个简单的委托:此委托所绑定的方法必须有一个string类型的参数,其返回类型必须为void
public delegate void DoHomeWork (string style);
//定义一个类,类里实现两种做作业方法
public class HomeWork
{
private string homework;
private bool IsByMyself;
public HomeWork(string homeworkcontent,bool IsLonely)
{
this.homework = homeworkcontent;
this.IsByMyself = IsLonely;
}
public void DoHomeWorkByMyself(string s)
{
if(this.IsByMyself == true)
{
Console.WriteLine("今晚的" + this.homework + "作业是我自己做的!" + s );
}
else
{
Console.WriteLine("今晚的" + this.homework + "作业是不我自己做的,还需努力!");
}
}
public void DoHomeWorkWithMom(string s)
{
if(this.IsByMyself == false)
{
Console.WriteLine("今晚的" + this.homework + "作业是妈妈监督我做的!" + s );
}
}
}
//使用委托绑定方法,在main方法中执行
//为了标准,main方法的类中需要定义一个调用委托的方法,当然也可以直接在main方法中进行调用
//在main方法中直接调用可写成DoMyHomeWork(word1)
public static void InvokeDoHomeWork(DoHomeWork action, string word)
{
action(word);
}
static void Main(string[] args)
{
string content = "数学";
bool Alone = false;
HomeWork MyHomeWork = new HomeWork(content,Alone);
string word1 = "我真棒!";
DoHomeWork DoMyHomeWork = new DoHomeWork(MyHomeWork.DoHomeWorkByMyself);
//当然,上述也可表示为DoHomeWork DoMyHomeWork = MyHomeWork.DoHomeWorkByMyself
InvokeDoHomeWork(DoMyHomeWork,word1);//调用委托
string word2 = "谢谢妈妈!";
DoMyHomeWork -= MyHomeWork.DoHomeWorkByMyself;//删除委托所绑定的方法
DoMyHomeWork += MyHomeWork.DoHomeWorkWithMom;//重新为委托绑定方法
InvokeDoHomeWork(DoMyHomeWork,word2);//调用委托
}
执行此段代码后,控制台程序结果如下
当然有人会说,我如果直接将HomeWork类实例化,通过实例化后的对象直接调用这两个函数,也会显示出同样的结果,这样岂不是更简单?
仅针对此例,事实是这样。但是此例仅是为了简单的展示委托的使用方法,并不典型。
委托的其他定义方法
委托还有其余的构建方法,比较典型的有如下两种:
Func<T> 委托名 = 调用的方法。
Action<T> 委托名 = 调用的方法。
两者不同处在于
Action<T> 定义的委托必定不会带有返回值,只会返回void类型。
Func<T> 定义的委托必定会带有返回值,不可返回void类型。
两者相同之处在于
Action<T> 和 Func<T>都可绑定带有参数的方法,方法最多可带有16个参数。
拿上面的例子来说,可将委托构建方式改为Action<string> DoHomeWork = MyHomeWork.DoHomeWorkByMyself,由于绑定的方法不具有返回类型,所以不可使用Func<T>的方式来构建委托。
委托绑定之匿名方法
这里介绍匿名方法的使用时直接采用lambda表达式,就不介绍delegate{方法体}的方式了,毕竟有了lambda表达式这种方便的方式谁还会去用老掉牙的方法呢,不多说,直接根据上述所举的例子进行变换。
Action<string> DoMyHomeWork = (str) =>MyHomeWork.DoHomeWorkByMyself(str);
当然,如果绑定的方法带有返回参数的话,也可采用Func<T>的方式,本例不适用。
多播委托
简单的来说就是一个委托绑定了多个方法,在调用委托时,会依次调用绑定的方法。故在使用时会有一定的风险:即在一次调用若干个方法时,其中一个方法出现了异常,则会导致之后的方法无法继续调用。
解决方法是先通过Delegate[] delegates = 委托.GetInvocationList()的方式获取到所有委托,再使用循环一次遍历委托,遇到异常使用Try..Catch捕获后仍然可以继续调用剩下的委托。
委托的内容基本上到这里就告一段落了。下面,我来用例子来形象化的解释事件和委托的关系及用法。
事件
简单的阐述一下我的理解,事件和委托之间我认为有相关联的四种角色:观察者,事件载体,事件,委托和方法。即观察者观察到事件载体有某事件发生,则进行委托,委托采用绑定的方法进行一系列的操作。
下面我自己构建一个业务场景,并使用委托与事件进行场景实现,以便于加深理解事件和委托的关系。
业务场景:汽车价格降低(打7折),打听到折价的消息后消费者向汽车销售人员求证,得到准确答复后立即准备去买车。
分析:
此业务场景有两个事件。
1.汽车价格降低消息传出。
2.汽车销售人员证实消息真实性并给出答复。
消费者有两个动作(即之后根据不同事件,委托所绑定的方法)。
1.打听汽车价格降低的真实性。
2.听到消息为真实消息后买车。
消费者为观察者。
事件监听载体(即被观察者)是汽车和销售人员。
故代码上实现为,被观察者需要提供其相应的监听事件,汽车为降价,销售人员为消息证真伪,观察者提供相应的方法,分别对应为询问消息真实性和购买汽车。
可通过以下代码实现。
//首先先定义两个事件监听托管。
public delegate string PriceDeclineHandler(object obj, bool IsReal);//价格降低事件监听
public delegate void ConfirmRealHandler(object obj, string s);//消息证真伪事件监听
//先定义一个观察者-消费者类。
public class Consumer
{
//向销售人员询证消息是否可靠。
public string AskingForAuthenticity(object obj, bool IsReal)
{
if (IsReal == true)
{
string s = "真实可靠";
return s;
}
else
{
string s = "虚假";
return s;
}
}
//根据消息是否可靠判断是否购买汽车。
public void Purchase(object obj, string s)
{
if (s == "真实可靠")
{
Console.WriteLine("降价消息" + s + ",我要买车!");
}
else
{
Console.WriteLine("降价消息" + s + ",傻子才去买车!");
}
}
}
//再定义被观察者-汽车类。
public class Car
{
//定义汽车类事件:降价-被价格降低事件监听者监听。
public event PriceDeclineHandler PriceDecline;
public Car()
{
PriceDecline = null;
}
//触发监听事件。
public string MarketSaturated(bool IsReal)
{
return PriceDecline(this, IsReal);
}
}
//再定义被观察者-销售人员类。
public class Saler
{
//定义销售人员类事件:消息证真伪-被消息证真伪事件监听者监听。
public event ConfirmRealHandler ConfirmReal;
public Saler()
{
ConfirmReal = null;
}
//触发监听事件。
public void Cofirming(string s)
{
ConfirmReal(this, s);
}
}
//在main方法中编写如下代码。
static void Main(string[] args)
{
bool MarketIsSaturated = false;
if (int.Parse(DateTime.Now.ToString("yyyy")) > 2019)
{
MarketIsSaturated = true;
}
Car MyCar = new Car();
Consumer MyConsumer = new Consumer();
//PriceDecline 事件绑定MyConsumer.AskingForAuthenticity方法。
MyCar.PriceDecline += new PriceDeclineHandler(MyConsumer.AskingForAuthenticity);
string s =MyCar.MarketSaturated(MarketIsSaturated);
Saler MySaler = new Saler();
//ConfirmReal 事件绑定MyConsumer.Purchase方法。
MySaler.ConfirmReal += new ConfirmRealHandler(MyConsumer.Purchase);
MySaler.Cofirming(s);
}
条件中可以清楚的看到,如果已经过了2019,说明市场已经饱和.当前时间是2021年,所以市场已经饱和,故汽车降价消息为真,最终结果应为降价消息真实可靠,我要买车!
执行代码后,结果如下。
以上是我对事件在代码端的一些尝试。