当前位置: 首页 > news >正文

C# 委托,事件和Lambda表达式

关于这个论题,

Delegates, Events, and Lambda Expressions 对此有比较深入的分析,可以参考。

C# vs C++之一:委托 vs 函数指针 比较了委托和C++指针的区别。

.NET 中的委托确实和C/C++的函数指针非常相似。它是一个值类型,它包装了一个指向方法的引用。它的作用也是为了能够将方法和变量一样作为参数传递。委托的典型应用是控件的事件处理方法。很显然,一个控件在设计的时候没有办法知道当特定事件发生的时候,需要什么方法来处理,这就需要将方法作为参数传递给控件。在LINQ中,也大量用到了委托。

声明一个委托要使用delegate关键字,如下:

delegate int Echo(string message);

这句代码声明了一个委托类型,这个委托类型的实例可以接受参数为string,返回值为int型的函数。这个方法可以是对象的方法,也可以静态方法,还可以是匿名方法,只要方法的签名和返回值是和声明一致的。这和C的函数指针很像,但是函数指针仅仅包含函数入口地址,而委托是一个类型,它具有比函数指针更强的功能。其中一点就是当方法是实例方法的时候,这个方法可以获得对象的其他变量的值,文首的第二篇文章对此有详细介绍,不再赘述。第二点就是委托是支持多播的,也就是一串方法可以可以依次被执行。例如:

static int EchoOriginal(string message)
{
    Console.WriteLine(message);
    return 1;
}

static int EchoReverse(string message)
{
    StringBuilder sb=new StringBuilder();
    for(int i=message.Length-1;i>=0;i--)             
        sb.Append(message[i]);             
    Console.WriteLine(sb.ToString());
    return -1;
}        

static void Main(string[] args)
{
    Echo eo = EchoOriginal;
    Echo er = EchoReverse;
    Echo all = eo + er;
    eo("Hello world");
    int i=all("Hello Delegate");
    Console.WriteLine(i);
}

我们定义两个方法,这两个方法都符合Echo的声明,最后Echo的all实例可以接受两个委托,调用all的时候,eo,er会被一次钓鱼,返回值是最后一个委托的返回值。程序的输出是:

Hello world
Hello Delegate
etageleD olleH
-1

事实上,方法并不需要和委托声明类型的签名完全一致,.net允许方法的返回值是继承自声明的返回值的类型,方法的参数类型是声明的参数的父类型。这就是Covariance and Contravariance in Delegates.

.NET的事件机制是以委托为基础的。事件机制有两部分组成,一部分是事件发布者,一部分是事件响应者。其实现原理就是由事件发布者声明一个委托对象,由事件响应者向那个委托挂载具体的处理方法,事件发布者在需要的时候调用这个委托,这样响应者的代码就会被执行。事实上,.NET也是这么做的。C#的event关键字就仅仅做了少量的工作,其中包括为类生成一个私有的delegate. event所支持的委托是有限制的委托,它的返回值必须是void,参数是两个,第一个是事件发生者,第二个参数是事件需要携带的参数。最简单的事件处理委托.net已经声明了:

 public delegate void EventHandler(
    Object sender,    EventArgs e )
 
声明事件的基本方式是 event 委托类型 事件名称;

举个例子,有这样的类,每当找到一个奇数,他就会触发一个事件。我们的程序在接到这个事件的时候在屏幕输出一个提示。类的代码可以这样实现:

public class OddFinder
   {
       public event EventHandler FindOdd;

       public void Find(int from, int to)
       {
           for (int i = from; i <= to; i++)
           {
               if (i % 2 != 0)
                   if (FindOdd != null)
                       FindOdd(this, EventArgs.Empty);
           }
       }
   }

这个类很简单,展示了发起事件的基本方法。首先声明一个事件,指明这个事件处理函数的委托类型。在需要触发事件的时候,首先判断是否有事件处理函数挂载,然后调用这个委托即可。外部处理程序把事件处理程序挂载上去:

static void Main(string[] args)
{
     OddFinder f = new OddFinder();
     f.FindOdd += new EventHandler(f_FindOdd);
     f.Find(1, 5);
}

static void f_FindOdd(object sender, EventArgs e)
{
    Console.WriteLine("Found!");
}

这样程序运行后,就会在屏幕上输出3次Found!。如果需要在触发事件的时候,传递更多的信息给事件处理函数,比如当前找到的奇数是多少,那么就需要新建一个类继承自EventArgs,在这个类中可以添加一些需要的数据。 再声明一个委托,第二个参数为EventArgs类型即可。

以上是基本的委托和事件的介绍,自.net 1.0开始就是如此,.net 2.0 引入了匿名方法,可以简化委托的某些操作。例如:

f.FindOdd += delegate(object sender, EventArgs e)
{
    Console.WriteLine("Found!");
};

匿名方法使用delegate关键字加上参数表,最后是代码块来定义。它可以作为委托赋值给委托类型。它可以省去单独定义一个方法的麻烦。

.net 3.0之后引入了Lambda表达式,它进一步简化了匿名方法的写法,使得在C#中,把函数作为参数传递变得更加简单自然,从而C#变得更加具有函数式语言的味道。关于函数式语言的进一步介绍,可以参考:Functional Programming Languages . 函数式语言的理论基础是Lambda Calulus,关于此可以参考A Tutorial Introduction to the Lambda Calculus .

Lambda表达式本质上还是匿名方法,它的一般形式是:

(input parameters) => expression

左侧是参数列表,=>右侧是方法体,可以是一个表达式(expression lambda),也可以是大括号括起来的语句段(statement lambda)。它省略了delegate关键字,使得代码更加紧凑。例如:

n=>n%2==0;

等价于

delegate(int n){ return n%2==0;}

expression lambda 广泛应用于LINQ,它可以用来构造Expression Tree,Expression Tree是LINQ的基础。可以通过动态构造Expression Tree来实现复杂的动态LINQ查询,不过这种方法虽然通用,对于数据库查询,使用起来和传统的拼接字符串相比还是很麻烦。下文将介绍微软的一个LINQ扩展,Dynamic LINQ.

 

 

    f.FindOdd  +=  (Object sender, EventArgs e)  =>
            {
                Console.WriteLine(
" Found! " );
                Console.WriteLine(
" Found2! " );
            };

 

转自:http://www.cnblogs.com/yinzixin/archive/2010/09/14/1825611.html

转载于:https://www.cnblogs.com/jhxk/articles/1826156.html

相关文章:

  • ES6学习笔记四 default、rest、Multi-line Strings
  • SharePoint 2010 Excel Service 入门——在网页上显示Excel内容
  • New Concept English Two 33 94
  • 100%高度全屏自适应
  • Django REST框架--关系和超链接api
  • sql语句的字段转成Date
  • [转]java按指定编码写入和读取文件内容的类
  • Android开发者应该深入学习的10个开源应用项目
  • centos7.3 docker升级
  • volatile和synchronized的区别
  • 全国省市数据库 access 版
  • CodeSmith模板引擎系列-目录
  • 分布式术语
  • SQL中truncate table和delete的区别
  • Ruby on Rails (ROR)类书籍下载地址及其他(整理)
  • EventListener原理
  • Fundebug计费标准解释:事件数是如何定义的?
  • HTTP中GET与POST的区别 99%的错误认识
  • Invalidate和postInvalidate的区别
  • maven工程打包jar以及java jar命令的classpath使用
  • Netty源码解析1-Buffer
  • Redis字符串类型内部编码剖析
  • TCP拥塞控制
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • Webpack 4x 之路 ( 四 )
  • 搭建gitbook 和 访问权限认证
  • 关于List、List?、ListObject的区别
  • 理解在java “”i=i++;”所发生的事情
  • 实习面试笔记
  • 算法-图和图算法
  • 小程序 setData 学问多
  • 小程序开发之路(一)
  • 在electron中实现跨域请求,无需更改服务器端设置
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • 宾利慕尚创始人典藏版国内首秀,2025年前实现全系车型电动化 | 2019上海车展 ...
  • "无招胜有招"nbsp;史上最全的互…
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • %3cli%3e连接html页面,html+canvas实现屏幕截取
  • (10)STL算法之搜索(二) 二分查找
  • (cos^2 X)的定积分,求积分 ∫sin^2(x) dx
  • (C语言)逆序输出字符串
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (附源码)spring boot网络空间安全实验教学示范中心网站 毕业设计 111454
  • (黑客游戏)HackTheGame1.21 过关攻略
  • (力扣)循环队列的实现与详解(C语言)
  • (一)Neo4j下载安装以及初次使用
  • (转)从零实现3D图像引擎:(8)参数化直线与3D平面函数库
  • (转)使用VMware vSphere标准交换机设置网络连接
  • .Net FrameWork总结
  • .NET3.5下用Lambda简化跨线程访问窗体控件,避免繁复的delegate,Invoke(转)
  • .net6使用Sejil可视化日志
  • .NET单元测试
  • .NET命名规范和开发约定
  • .net中的Queue和Stack