委托、事件和Lambda
一、委托 delegate
1.在.Net平台下,委托类型用来定义和响应应用程序中的回调。事实上,.Net委托类型是一个类型安全的对象,指向可以以后调用的其他方法,.Net委托是内置支持
多路广播和异步方法调用的对象。.Net委托是类型安全的,如果将一个不“匹配模式”的方法传入委托,将会收到编译器错误。
2.委托类型包含3个重要的信息:
(1)它所调用的方法的名称;
(2)该方法的参数(可选);
(3)该方法的返回值(可选)。
.Net委托既可指向静态方法,也可以指向实例方法。
3.定义委托类型:public delegate int BinaryOp(int x, int y);
经过反编译:委托类型BinaryOp自动产生一个派生自System.MulticastDelegate(这是一个抽象类)的密封类,并生产3个方法:
(1)public virtual int Invoke(int x, int y);它被用来以同步方式调用委托对象维护的每个方法。
(2)public virtual IAsyncResult BeginInvoke(int x, int y, AsyncCallback callback, object @object);以异步方式调用委托对象维护的方法。
(3)public virtual int EndInvoke(IAsyncResult result);
4.委托还可以指向包含任意数量out或ref参数(以及用params关键字标记的数组参数)的方法。
5.System.MulticastDelegate 和 System.Delegate
我们永远不会直接派生自这些基类,如果我们使用delegate关键字,就间接创建了一个类,这个类“是” MulticastDelegate。
所有委托类型都共有的核心成员:
Method:此属性方法MethodInfo对象,用以表示委托维护的静态方法的详细信息。
Target:如果方法调用是定义在对象级别的(而不是静态方法),Target返回表示委托维护的方法的对象。如果Targe返回值为null,调用的方法是一个静态方法。
Combine():此静态方法给委托维护的列表添加一个方法。在C#中,使用重载+=操作符作为简化符号调用此方法。
GetInvocationList():此方法返回一个System.Delegate类型的数组,其中数组中的每个元素都表示一个可调用的特定方法。
Remove(),RemoveAll():这些静态方法从调用列表中移除一个(或所有)方法,在C#中,Remove()方法可通过使用重载-=操作符来调用。
6.使用委托发送对象状态通知
(1)定义将通知发送给调用者的委托类型。
(2)声明类中每个委托类型的成员变量。
(3)在类上创建辅助函数使调用者能指定由委托成员变量保存的方法。(注册函数)
(4)在类内部的某个方法中调用委托的方法列表。
示例:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
DoSomething d = new DoSomething("打地基");
d.RegisterWorkEngineHandler(WorkEvent); for (int i = ; i <= ; i++)
{
d.Work(i);
} Console.ReadKey();
} static void WorkEvent(string msg)
{
Console.WriteLine(msg);
}
} class DoSomething
{
public string WorkName { get; set; } public DoSomething(string workname)
{
this.WorkName = workname;
} public void Work(int step)
{
this.handler(string.Format("{0}工作:正在做第{1}步事情...", this.WorkName, step));
} // 定义委托类型
public delegate void WorkEngineHandler(string msg); // 声明私有的委托类型变量
private WorkEngineHandler handler; // 注册委托对象
public void RegisterWorkEngineHandler(WorkEngineHandler handler)
{
this.handler = handler;
}
} }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
DoSomething d = new DoSomething("打地基");
d.RegisterWorkEngineHandler(WorkEvent); for (int i = ; i <= ; i++)
{
d.Work(i);
} Console.ReadKey();
} static void WorkEvent(string msg)
{
Console.WriteLine(msg);
}
} class DoSomething
{
public string WorkName { get; set; } public DoSomething(string workname)
{
this.WorkName = workname;
} public void Work(int step)
{
this.handler(string.Format("{0}工作:正在做第{1}步事情...", this.WorkName, step));
} // 定义委托类型
public delegate void WorkEngineHandler(string msg); // 声明私有的委托类型变量
private WorkEngineHandler handler; // 注册委托对象
public void RegisterWorkEngineHandler(WorkEngineHandler handler)
{
this.handler = handler;
}
} }
7.方法组转换语法:该特性允许我们在调用以委托作为参数的方法时直接提供方法的名称,而不是创建委托对象。
8.委托协变:因为委托是安全类型,它们不遵守继承的基本规则。协变允许我们构建一个委托,能指向返回类及相关继承体系的方法。
逆变:允许我们创建一个委托,指向多个方法,方法的参数是存在传统继承关系的对象。
9.泛型委托:通过类型参数来构建
public delegate void MyGenericDelegate<T>(T arg);
在不使用泛型的情况下模拟泛型委托:public delegate void MyDelegate(object arg);尽管这样可以把任何类型的数据发送到委托目标,但是会因此
失去类型安全并且可能还会有装箱/拆箱损失
二、事件 event
从头使用委托会有一些重复代码:定义委托,声明必要的成员变量以及创建自定义的注册/注销方法来保存封装等。
1.为了简化自定义方法的构建来为委托调用列表增加和删除方法,C#提供了event关键字。在编译器处理event关键字的时候,它会自动提供注册和注销方法以及委托类型
任何必要的成员变量。这些委托成员变量总是声明为私有的,因此不能直接从触发事件的对象访问它们。
2.定义一个事件分为两个步骤:首先,我们需要定义一个委托类型,它包含在事件触发时将要调用的方法。其次,通过C# evnet关键字用相关委托声明这个事件。
3.C#事件事实上会扩展为两个隐藏的公共方法,一个带add_前缀,另一个带remove_前缀,前缀后面是C#事件的名称。
4..Net基础类库底层委托的第一个参数是一个System.Object,第二个参数是派生自System.EventArgs的子类型。System.Object参数表示一个对发送事件的对象的引用,
第二个参数则表示与该事件相关的信息。
5.泛型EventHandler<T>委托
由于很多自定义委托接受object作为第一个参数,EventArgs派生类型作为第二个参数,我们可以通过使用泛型EventHandler<T>类型来进一步简化
public event EventHandler<MyEventArgs> Exploded;
6.按钮的单击事件
// 摘要:
// 表示将处理不包含事件数据的事件的方法。
//
// 参数:
// sender:
// 事件源。
//
// e:
// 不包含任何事件数据的 System.EventArgs。
[Serializable]
[ComVisible(true)]
public delegate void EventHandler(object sender, EventArgs e); //
// 摘要:
// 在单击控件时发生。
public event EventHandler Click; this.button1.Click += new System.EventHandler(this.button1_Click);
private void button1_Click(object sender, EventArgs e)
{ }
三、Lambda表达式
1.手工定义一个由委托对象调用的方法显得有点繁琐,现在可以在事件注册时直接将一个委托与一段代码相关联,这种代码的正式名称称为匿名方法。
t.SomeEvent += delegate(参数){};
2.匿名方法不能访问定义方法中的ref或out参数;匿名方法中的本地变量不能与外部方法中的本地变量重名;匿名方法可以访问外部类作用域中的实例变量(或静态变量)。
3.Lambda表达式只是用更简单的方法来写匿名方法,彻底简化了对.Net委托类型的使用。
4.Lambda表达式规则:首先定义一个参数列表(0个或多个),“=>”标记紧随其后,然后就是处理这些参数的语句。
委托、事件和Lambda的更多相关文章
- 9、委托、事件、Lambda
开始 关于委托,肯定是要有问题的. 第一个问题,委托用来干什么? 看.net中的表述:在.net平台下,委托类型用来定义和相应应用程序中的回调.(回调?处理内存中两个实体双向通信的一种技术.) 第 ...
- 委托,事件,lambda表达式
开篇说明三个点: 委托是一种类型 事件是委托的实例 lambda表达式是一个方法(匿名方法) [未完待续]
- C# ~ 从 委托事件 到 观察者模式 - Observer
委托和事件的部分基础知识可参见 C#/.NET 基础学习 之 [委托-事件] 部分: 参考 [1]. 初识事件 到 自定义事件: [2]. 从类型不安全的委托 到 类型安全的事件: [3]. 函数指针 ...
- Unity C#笔记 委托&事件
C#的委托与事件搭配,即是观察者模式的一种实现. 因为观察者模式的原理很易懂,不作多讲,本文纯粹用于记录语法. delegate(委托) //声明没有参数,没有返回值的委托类型XXXX public ...
- Delegate,Action,Func,匿名方法,匿名委托,事件 (转载)
Delegate,Action,Func,匿名方法,匿名委托,事件 (转载) 一.委托Delegate 一般的方法(Method)中,我们的参数总是string,int,DateTime...这些基本 ...
- C#委托,事件理解入门 (译稿)
原文地址:http://www.codeproject.com/Articles/4773/Events-and-Delegates-Simplified 引用翻译地址:http://www.cnbl ...
- 关于ios使用jquery的on,委托事件失效
$('.parents').on("click",'.child',function(){}); 类似上面这种,在ios上点击"child"元素不会起作用,解决 ...
- Observer设计模式中-委托事件-应用在消息在窗体上显示
Observer设计模式:监视者模式.在类中的方法中处理的结果或者消息通过事件委托 的方式发送给主窗体. 因为在其它类中直接访问主窗体类,显示内容是不能直接调用控件赋值的,当然也有别的类似查阅控件名, ...
- Asp.net用户控件和委托事件
在Asp.net系统制作过程中,门户类型的网站,我们可以用DIV+CSS+JS+Ajax全部搞定,但是一旦遇到界面元素比较复杂的时候,还是UserControl比较方便一些,各种封装,各种处理,然后拖 ...
- jQuery里面的普通绑定事件和on委托事件
以click事件为例: 普通绑定事件:$('.btn1').click(function(){}绑定 on绑定事件:$(document).on('click','.btn2',function(){ ...
随机推荐
- redis的数据类型
redis有string,hash,list,sets.zsets几种数据类型 1.string数据类型 可包含任何数据,是二进制安全的,比如图片或者序列化的对象set key valueset na ...
- 图片加载与缓存利器(自动缓存)--第三方开源-- Glide
Android Glide使用便利,短短几行简单明晰的代码,即可完成大多数图片从网络(或者本地)加载.显示的功能需求. 使用Android Glide,需要先下载Android Glide的库,And ...
- MySQL下查看用户和建立用户
启动数据库: [root@server ~]# mysqld_safe & [1] 3289 [root@server ~]# 130913 08:19:58 mysqld_safe Logg ...
- 十天学会单片机Day3 D/A与A/D转换器
D/A转换器 1.二进制权电阻网络型D/A转换器 基准电压Vref 数据D(d3d2d1d0) 输出模拟电压V0 i0 = Vref/8R i1 = Vref/4R i2 = Vref/ ...
- iOS开发常用的宏
#define SCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)#define SCREEN_HEIGHT ([UIScreen mainS ...
- Python脚本控制的WebDriver 常用操作 <七>浏览器前进和后退操作
下面将使用WebDriver来控制浏览器的前进和后退操作 测试用例场景 此操作和get.url()方法功能相同 Python脚本 # coding=gbk ''' Created on 2013年12 ...
- .NET开源工作流RoadFlow-流程设计-流程步骤设置-基本设置
流程属性设置完成后点击确定之后,即可进行流程步骤设置了. 点击工具栏上的步骤按钮,即可添加一个新步骤. 在新步骤图形上双击即可弹出该步骤相应属性设置框. 步骤ID:系统自动为该步骤生成的唯一ID. 步 ...
- 【转】C# 子窗体如何调用父窗体的方法
网络上有几种方法,先总结如下: 调用窗体(父):FormFather,被调用窗体(子):FormSub. 方法1: 所有权法 //FormFather: //需要有一个公共的 ...
- Lex+YACC详解
1. 简介 只要你在Unix环境中写过程序,你必定会邂逅神秘的Lex&YACC,就如GNU/Linux用户所熟知的Flex&Bison,这里的Flex就是由Vern Paxon实现的一 ...
- hdu 1873 看病要排队
题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1873 看病要排队 Description 看病要排队这个是地球人都知道的常识.不过经过细心的0068的 ...