自我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年,所以市场已经饱和,故汽车降价消息为真,最终结果应为降价消息真实可靠,我要买车!

执行代码后,结果如下。

技术图片

 

 

 以上是我对事件在代码端的一些尝试。

 

到目前为止,关于委托和事件方面的内容,我理解的深度仅上所述,应该还有一些我没有了解到的知识或者我的上述内容有些是理解错误的,我希望看到我写的者片随笔的技术大牛们给我提出宝贵的建议,有错误及时给我提出来,会让我受益匪浅

相关文章