.NET面试题系列(十)委托与事件

委托

有了委托的存在,使得方法可以作为参数传递给另一个方法。

int Max(int x,int y)
{
    return x>y?x:y;
}
int Min(int x,int y)
{
    return x<y?x:y;
}

    上面两个函数的共同特点:具有相同的返回值和参数列表。在C++里,我们使用函数指针来指代(被授权,代表)这两个函数。

  实际上,我们可以用函数指针指向任意一个具有相同返回值和参数列表的函数(静态方法或实例的方法成员)。

     在C#里没有提供函数指针,取而代之的是委托(delegate);利用委托,我们可以像使用函数指针一样在程序运行时动态指向具备相同签名(具有相同参数类型、参数个数以及相同类型返回值)的方法。

    委托的本质:函数指针。说的通俗一些,委托就是能够让方法作为变量来传递。

    委托是一种类型安全的函数回调机制, 它不仅能够调用实例方法,也能调用静态方法,并且具备按顺序执行多个方法的能力。

 委托的声明

Demo1

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

namespace DelegateSamples
{
    //声明一个委托,参数为string,无返回值
    delegate void DelSamples(string msg);
    class Program
    {
        static void Main(string[] args)
        {
          //使用new关键字
          DelSamples delSample = new DelSamples(new Program().SpeakChinese);
          delSample("Koala工作室");

          //不使用new,自动推断委托类型
          DelSamples delSample2 = SpeakEnglish;
          delSample2("KoalaStudio");

          //利用Lambda表达式
          DelSamples delSample3 = (string msg) => SpeakEnglish(msg);
          delSample3("KoalaStudio");

          Console.ReadKey();
        }

        private void SpeakChinese(string msg)
        {
            Console.WriteLine("你好,我是{0}",msg);
        }

        private static void SpeakEnglish(string msg)
        {
            Console.WriteLine("Hello,I'm {0}",msg);
        }
    }
}

Demo2

private static void EnglishGreeting(string name)
        {
            Console.WriteLine("Good Morning, " + name);
        }

        private static void ChineseGreeting(string name)
        {
            Console.WriteLine("早上好, " + name);
        }
        public delegate void GreetingDelegate(string name);//委托
        private static void GreetPeople(string name, GreetingDelegate MakeGreeting)
        {
            MakeGreeting(name);
        }
        private void button45_Click(object sender, EventArgs e)
        {
            GreetPeople("Liker", EnglishGreeting);
            GreetPeople("沐风", ChineseGreeting);
            Console.ReadLine();
        }

  我们现在对委托做一个总结:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If … Else(Switch)语句,同时使得程序具有更好的可扩展性。

匿名委托

  在上一分钟已经知道了,完成一个委托应用分三步走,缺一步都不行,如果要跨大步,当心步子大了扯着蛋。但是微软不怕扯着蛋,非要把三步做成两步来走啊!所以微软就用匿名方法来简化上边的三个步骤。匿名方法这个玩意儿怎么说呢,在C#中完全是可有可无的东西,只是为C#锦上添花,有人别出心裁给它取个名字叫语法糖。

 1 public partial class WebForm3 : System.Web.UI.Page
 2 {
 3     //step01:首先用delegate定义一个委托 
 4     public delegate int CalculatorAdd(int x, int y);
 5 
 6     protected void Page_Load(object sender, EventArgs e)
 7     {
 8         //step02:用这样的写法 delegate(int x, int y) { return x + y; },把一个方法赋值给委托
 9         CalculatorAdd cAdd = delegate(int x, int y) { return x + y; };
10         int result = cAdd.Invoke(5, 6);
11     }
12 }
View Code

step01:首先用delegate定义一个委托 。

step02:用这样的写法 delegate(int x, int y) { return x + y; },把一个方法赋值给委托,其实这种写法就是匿名方法。

这时会惊奇的发现,这不是三步当着两步走了哇?

匿名委托的写法更加优雅,但是需要注意两点:

    1、在函数内部不能使用跳转语句跳出函数外部;

    2、不能使用ref和out等关键字

Lambda表达式实现匿名委托

  原本很简单的程序,加上几个delegate关键字,这代码一下就变得深奥了,深奥的东西懂的人就变少了,所以这个还可以作为加薪的筹码。但是微软对C#的设计理念是简单易用。微软就想方设法的来简化delegate(int x, int y) { return x + y; }这个匿名方法,Lambda就出现了。下边我来看几种lambda表达式的写法:

 1 public partial class WebForm3 : System.Web.UI.Page
 2 {
 3     public delegate int CalculatorAdd(int x, int y);
 4 
 5     protected void Page_Load(object sender, EventArgs e)
 6     {
 7         //方法一:
 8         CalculatorAdd cAdd1 = (int x, int y) => { return x + y; };
 9         int result1 = cAdd1(5, 6);
10 
11         //方法二:
12         CalculatorAdd cAdd2 = (x, y) => { return x + y; };
13         int result2 = cAdd2(5, 6);
14 
15         //方法三:
16         CalculatorAdd cAdd3 = (x, y) => x + y;
17         int result3 = cAdd2(5, 6);
18     }
19 }
View Code

方法一:简单的把delegate去掉,在()与{}之间加上 "=>"。

方法二:在方法一的基础上把参数类型都干掉了。

方法三:要干就干彻底些,把{},以及return关键字都去掉了。

这几种方法随便怎么写都行,不过就是害苦了初学者,一会儿看到这种写法,一会儿看到那种写法,把人搞的神魂颠倒人,如果没人指点,确实会迷糊,难就难在这儿。

多播委托

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

namespace DelegateSamples
{
    //声明一个委托,参数为string,无返回值
    delegate void DelSamples(string msg);
    class Program
    {
        static void Main(string[] args)
        {
            //多播委托可以带返回值,但是只有最后一个方法的返回值会被返回。
            DelSamples delSample6 = new Program().SpeakChinese;
            delSample6 += SpeakEnglish;
            delSample6("KoalaStudio");

            Console.ReadKey();
        }

        private void SpeakChinese(string msg)
        {
            Console.WriteLine("你好,我是{0}",msg);
        }

        private static void SpeakEnglish(string msg)
        {
            Console.WriteLine("Hello,I'm {0}",msg);
        }
    }
}

  多播委托可以连续执行函数,但是如果函数有返回值,那只有最后一个函数的返回值会被正确返回.

泛型委托

泛型委托包括Action、FuncPredicate三种委托。
1.
Action
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DelegateSamples
{
    class Program
    {
        static void Main(string[] args)
        {
            /* Action<T>:封装只有一个参数(类型为T),不包括返回值的签名函数,它包括以下几种情况:
             * Action<T>、Action<T1,T2>、Action<T1,T2,T3>、Action<T1,T2,T3,T4>
             * 声明:
             * delegate void Action();
             * delegate void Action<T1>(T1 arg1);
             * delegate void Action<T1,T2>(T1 arg1,T2 arg2);
             * delegate void Action<T1,T2,T3>(T1 arg1,T2 arg2,T3 arg3);
             * delegate void Action<T1,T2,T3,T4>(T1 arg1,T2 arg2,T3 arg3,T4 arg4);
             */
            Action<string> action = SpeakEnglish;
            action("KoalaStudio");

            Action<string, string> action2 = SpeakTwoLanguage;
            action2("KoalaStudio","Koala工作室");
            Console.ReadKey();
        }

        private void SpeakChinese(string msg)
        {
            Console.WriteLine("你好,我是{0}",msg);
        }

        private static void SpeakEnglish(string msg)
        {
            Console.WriteLine("Hello,I'm {0}",msg);
        }

        private static void SpeakTwoLanguage(string msg1, string msg2)
        {
            Console.WriteLine("你好,我是{0}",msg1);
            Console.WriteLine("Hello,I'm {0}",msg2);
        }
    }
}

2.Func

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

namespace DelegateSamples
{
    class Program
    {
        static void Main(string[] args)
        {
            /* Func<T,TResult>:封装一个具有参数(类型为T),返回TResult类型值的签名函数,它包括以下几种情况:
             * Func<T,TResult>、Func<T1,T2,TResult>、Func<T1,T2,T3,TResult>、Func<T1,T2,T3,T4,TResult>
             * 声明:
             * ……略去
             */
            Func<string,string/*这是返回值类型*/> func = SpeakEnglish;
            func("KoalaStudio");

            Func<string, string, string/*这是返回值类型*/> func2 = SpeakTwoLanguage;
            func2("KoalaStudio","Koala工作室");
            Console.ReadKey();
        }

        private static string SpeakEnglish(string msg)
        {
            return string.Format("Hello,I'm {0}", msg);
        }

        private static string SpeakTwoLanguage(string msg1, string msg2)
        {
            Console.WriteLine("你好,我是{0}",msg1);
            Console.WriteLine("Hello,I'm {0}",msg2);
            return string.Format("你好,我是{0};Hello,I'm {1}", msg1,msg2);
        }
    }
}

 3.Predicate

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

namespace DelegateSamples
{
    class Program
    {
        static void Main(string[] args)
        {
            /* bool Predicate<T>:表示定义一组条件并确定指定对象是否符合这些条件的方法。
             * 通常,此类委托由Array和List类的几种方法使用,用于在集合中搜索元素。
             * delegate bool Predicate<T>(T obj),如果obj符合此委托表示的方法中定义的条件,则返回true,否则返回false
             */

            List<string> listString = new List<string>()
            {
                "a","abc","koala","xyz","take"
            };

            //List对象的FindAll定义:public List<T> FindAll(Predicate<T> match);
            //match 类型:System.Predicate<T> 委托,用于定义要搜索的元素应满足的条件。
            //返回值
            //类型:System.Collections.Generic.List<T>
            //如果找到,则为一个 List<T>,其中包含与指定谓词所定义的条件相匹配的所有元素;否则为一个空 List<T>。
            Predicate<String> match = delegate(string word)
            {
               if (word.Length > 4)
               {
                   return true;
               }
               return false;
            };

            List<string> result = listString.FindAll(match);
        }
    }
}

协变与逆变

   http://www.cnblogs.com/laoyu/archive/2013/01/13/2859000.html

异步委托

 

表达式树

 

 

委托与事件

1.事件与委托的关系

  委托是一种类型,使用委托可以将方法当成参数传递,这样做的好处是使程序之间的耦合降低,同时节省代码。

  事件是委托的一个实例

  事件中涉及两种角色——事件发布者和事件订阅者。 

  事件发布者: 触发事件的对象称为事件发布者。

  事件订阅者: 捕获事件并对其做出处理的对象称为事件订阅者。

委托、事件与Observer设计模式

public delegate void delegatename(int param); //声明委托
public event delegatename eventname; //声明事件

 

using System;
using System.Collections.Generic;
using System.Text;

namespace Delegate {
    // 热水器
    public class Heater {
       private int temperature;
       public delegate void BoilHandler(int param);   //声明委托
       public event BoilHandler BoilEvent;        //声明事件

       // 烧水
       public void BoilWater() {
           for (int i = 0; i <= 100; i++) {
              temperature = i;

              if (temperature > 95) {
                  if (BoilEvent != null) { //如果有对象注册
                      BoilEvent(temperature);  //调用所有注册对象的方法
                  }
              }
           }
       }
    }

    // 警报器
    public class Alarm {
       public void MakeAlert(int param) {
           Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", param);
       }
    }

    // 显示器
    public class Display {
       public static void ShowMsg(int param) { //静态方法
           Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", param);
       }
    }
    
    class Program {
       static void Main() {
           Heater heater = new Heater();
           Alarm alarm = new Alarm();

           heater.BoilEvent += alarm.MakeAlert;    //注册方法
           heater.BoilEvent += (new Alarm()).MakeAlert;   //给匿名对象注册方法
           heater.BoilEvent += Display.ShowMsg;       //注册静态方法

           heater.BoilWater();   //烧水,会自动调用注册过对象的方法
       }
    }
}

输出结果

 

 

 

 

 

 

 

http://www.tracefact.net/tech/009.html

转载于:https://www.cnblogs.com/cnki/p/5241506.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值