浅谈委托、委托与事件、委托进化成lambda表达式和Linq

时间:2022-01-13 18:58:21
   委托的重要性,在C#中是不言而喻的,理解起来也需反复琢磨,初学者可能觉得很难,但是迈过去这套槛后,也许就觉得容易了。本文结合自己的体会,将从委托、委托与事件、委托进化成lambda表达式,再到linq,浅谈自己的理解与体会,与大家交流交流,还望高手多多指点。真的很佩服微软这帮牛逼的人,一环套一环的设计,不知他们刚开始设计C#委托时是否就想到了未来将要产生的linq技术。

  我们先从单纯的委托开始研究。MSDN上给委托定义为:委托是一种定义方法签名的类型。 当实例化委托时,您可以将其实例与任何具有兼容签名的方法相关联。 您可以通过委托实例调用方法。 这三句话的意思表明了委托是一种类型,但是一种很特殊的类型,包含参数,返回值,一般类前面会有一个class修饰,但是委托前面用delegate;在实例化委托这个类型时需要与一个具体的方法相关联,而且这个方法的参数,返回值必须与委托定义的参数和返回值类型、个数一致。考虑现实中的一个现象,大贪官一般不自己*,会通过他的助手或者其他人间接*,这里贪官和他的助手之间会形成一个委托关系,大贪官可以定义成一个委托,助手*可以定义为一种方法。

     这里面为了方便先定义个贪官类、助手类、贪官委托

namespace testcsharp3._0
{
    public delegate void EventHandler();//大贪官委托,不包含任何参数,返回值为空
    class tanguan //大贪官类,里面还什么也没有
    {    }
}

namespace testcsharp3._0
{

    class zhushou   //定义助手类,里面包含一个静态方法,注意方法的参数和返回值与贪官的委托兼容
    {
        public static void suohui()
        {
            Console.WriteLine("贪官要出去旅游,通过助手帮其*1000元!");        
        }
    }
}

 static void Main(string[] args)
        {
            tanguan tg = new tanguan();//实力化一个贪官类
            EventHandler weituo = new EventHandler(zhushou.suohui);//实例化一个委托,与zhushou类中的suohui方法关联起来
            weituo();//执行委托
            Console.ReadLine();

        }

运行结果:贪官要出去旅游,通过助手帮其*1000元!

到此个最简单的委托例子就实现了,当然这个例子没有任何用处,呵呵。大家注意到,上面*这个动作实际上是通过main()函数里面的weituo()执行的。可能还是需要贪官给助手提示,助手才去*,这样的助手显然是不合理的。情况应该是一旦贪官缺钱花(这个事情发生可能使随机的),助手就要帮其去*,这样子贪官才会喜欢。与我们编程中点击一个按钮控件,就会触发一个事件一样。因此为了改进这种贪官的需求,我们有必要引入事件机制,一旦缺钱花的事件发生,助手就开始行动,呵呵!

 class tanguan
    {
        public delegate void EventHandler();//大贪官委托,不包含任何参数,返回值为空 
        public event EventHandler nomoney;//贪官缺钱事件,与官的委托联系起来

        public void nomoneyhappen()  
        {
            nomoney();//触发这个缺钱事件
        }
    }

 static void Main(string[] args)
        {
             tanguan tg = new tanguan();//实力化一个贪官类
             tg.nomoney += new tanguan.EventHandler(tg_nomoney);//注册一个缺钱的事件
            tg.nomoneyhappen();//执行函数,引发缺钱事件发生
            Console.ReadLine();

        }

        static void tg_nomoney()//缺钱事件发生后,要干的事情
        {
            zhushou.suohui();//调用助手来要钱
        }

运行后输出:贪官要出去旅游,通过助手帮其*1000元!

 上面的代码就是将事件与委托联系起来了,但是这样的时间没有任何的参数传递,假如一个助手要帮好几个官处理要钱的事情,他必须知道是谁缺钱了,当他要到钱后就可以给谁,所以这种情况下,触发的事件对象应该清楚。

        public string tanguanname { get; set; }//给贪官这个类加个名字属性,便于助手找
        public delegate void NameEventHandler(object sender, EventArgs e);//大贪官委托,包含一个object的参数和空的EventArgs参数,sender就是引起事件的对象
        public event NameEventHandler namenomoney;

        public void nomoneyhappen() 
        {
            //触发这个缺钱事件
            namenomoney(this, null);//传递当前对象过去
        }

 static void Main(string[] args)
        {
            tanguan tg = new tanguan() {tanguanname="A" };//实力化一个A贪官类       
            tg.namenomoney += new tanguan.NameEventHandler(tg_namenomoney); //注册一个缺钱的事件
            tg.nomoneyhappen();//执行函数,引发缺钱事件发生
            Console.ReadLine();

        }

        static void tg_namenomoney(object sender, EventArgs e)
        {
            tanguan t = sender as tanguan;//获得到底是哪个官缺钱
            Console.WriteLine(t.tanguanname);
            zhushou.suohui();
        }

上面的运行输出为:A  贪官要出去旅游,通过助手帮其*1000元!

如果把main()函数中的 tanguan tg = new tanguan() {tanguanname="B" };//实力化一个B贪官类  ,那么结果就是 B  贪官要出去旅游,通过助手帮其*1000元!

同样的如果想传递更多参数信息,我们可以继承EventArgs类,定义自己的参数类型,这样传递的信息会更丰富哦! 

通过上面的事件与委托代码 我们知道程序流程是:先定义一个委托,接着定义一个委托的事件,在调用方里面注册一个事件,并实例化一个委托,与这个注册的事件关联起来,定义委托的方法,在方法中执行你想要干的事情。 一旦我们触发了事件(这里我们通过代码触发的,像button这样的标准控件可以通过鼠标点击触发),就会执行委托的方法。

 下面我们研究委托的进化。像上面那些注册事件的代码 tg.namenomoney += new tanguan.NameEventHandler(tg_namenomoney); //注册一个缺钱的事件

发现这里的方法名称tg_namenomoney其实作用不大,C#中完全可以用匿名方法代替,因此代码可以改为

tg.namenomoney += delegate(object sender, EventArgs e)//匿名方法
            {
                 tanguan t = sender as tanguan;//获得到底是哪个官缺钱
            Console.WriteLine(t.tanguanname);
            zhushou.suohui();

            };

匿名方法的引用,让委托变得直接了当,不用绕个弯子了,至于 匿名方法编译成什么了,要研究下编译后的代码,这个还没仔细研究过,有待完善。。。

微软那帮人觉得上面那样搞代码还不够简洁,如是又产生了lambda表达式

tg.namenomoney += (object sender, EventArgs e)=>
            {
             tanguan t = sender as tanguan;//获得到底是哪个官缺钱
            Console.WriteLine(t.tanguanname);
            zhushou.suohui();

            };

这个和上面的执行结果一样。注意=>这个是在lambda里面产生的新的符号,左边表示参数列表,右边执行语句,lambda表达式的参数既可以是显示类型化的,也可以是隐式的。编译器可以更具上下文推断出来。在我看来 这些都是委托演变而来的,只不过微软把编译器做的更牛了,让我们少写了很多代码,编译器编译后的代码估计还原始的委托差不多(没有研究过,不知道对不对)

有了lambda表达式后,微软又整出来个隐式类型的东东,我们经常说C#是强类型语言,任何变量的定义都必须指定类型,但是在现实中有一种这样的情况,我们需要的是数据,并不关注他的类型是什么,从数据库检索出来的数据,也许和某个实体类属性是一一对应的,但是我们并不需要所有这些字段的数据,我们可能利用其中的两个字段的数据,以前的搞法是,在新定义一个类型,然后一个个实例化这个类,赋值给属性。现在如果有了隐式类型,我们就可以省下从新定义类的工作量了。如:var peoplename=“huxing”; 编译器会自动的推断出这个变量的类型是字符串,说到底还是编译器干的活,牛逼!

有了隐士类型,有了lambda表达式,微软就水到渠成的推出了Linq,先看下面这段代码

 public static List<person> listperson = new List<person>
        {
         new person{name="guoyuanwei",age=600},
         new person{name="wanjianbang",age=600},
         new person{name="xiaoyunxi",age=600},
         new person{name="huxing",age=600},
         new person{name="hujingtao",age=100},
         new person{name="wenjiabao",age=80},        
        };

        public static void showallperson(List<person> listp,int count)
        {
                 var man = from p in listp
                   where p.age > 300
                   select new { manname = p.name, manage = p.age };//lambda表达式,linq语句

         foreach(var v in man)
         {
             Console.WriteLine(v.manname + ":" + v.manage.ToString());
         }

      }

 如果在main()函数里面调用showallperson()就会输出 年龄大于300的人的名字,如果采取传统的c#代码,我们可能这样做

 把 showallperson部分改为用foreach遍历
  public static void showallperson(List<person> listp,int count)
        {

       foreach (person p in listp)
         {
             if(p.age>300)
             Console.WriteLine(p.name + ":" + p.age.ToString());
         }

  }

咋看上去代码还少些了,当然我的这个例子比较短,也许看不出啥来,但是在复杂的逻辑中lambda表达式就会显出他的威力。

好了,就写到这里,要干活了哦,时间紧,任务重啊!下次在整理整理,写的不好,望大家指正。