摘要:

  • 委托
  • Lambda表达式
  • 事件

1.委托是寻址方式的.NET版本。与C++的函数指针区别:委托时类型安全的类,定义了返回类型和参数的类型。

委托是一种特殊类型的对象,特殊之处在于,我们之前定义的所有对象都包含数据,而委托包含的只是一个或多个方法的地址。

2.声明委托

  • 定义要使用的委托,即告诉编译器这种类型的委托表示哪种类型的方法。(编译器在后台将创建表示该委托的类)
  • 创建该委托的一个或多个实例 。
delegate void IntMethodInvoker(int x);//必须提供方法的签名和返回类型等细节,类型安全性非常高

定义委托可以在定义类的任何相同地方定义,可以在另一个类的内部定义,也可以在任何类的外部定义,还可以在名称空间中把委托定义为顶层对象。

委托派生自基类System.MulticastDelegate,基类又派生自System.Delegate

与类不同的是,创建类的实例称为“对象”,而创建的委托的实例仍称为委托,必须从上下文确定其含义。

3.使用委托

在C#中,委托在语法上总是接受一个参数的构造函数,这个参数就是委托引用的方法。

private delegate string GetString();
static void Main()
{int x = 40;GetAString firstStringMethod = new GetAString(x.ToString);//ToString()是实例方法(不是静态),因此需要指定实例x和方法名来正确地使用委托
    ...
}

使用委托实例的名称,并通过括号中的等效参数来调用委托

Console.WriteLine("String is {0}",firstStringMethod());
//等价于
//Console.WriteLine("String is{0}",x.ToString());

也可以使用firstStringMethod.Invoke();来调用,是一样的。

委托推断:为了减少输入量,只要需要委托实例,可以只传送地址。

GetAString firstStringMethod = x.ToString;

委托推断在需要委托实例的任何地方使用,也可以用于事件。

使用委托的另外一种方式是:把方法组合到一个数组中,这样可以在循环中调用不同的方法。

delegate double DoubleOp(double x);
static void Main()
{DoubleOp[] operations = {MathOperations.MultipleByTwo,MathOperations.Square};...
}

4.Action<T>和Func<T>委托

Action<T>委托表示引用一个void返回类型的方法。因为这个委托类存在不同的变体,所以可以传递至多16种不同的参数类型。

  • Action<int T1,int T2>:调用带两个参数的方法

Func<T>委托允许调用带返回类型的方法,与Action<T>类型,Func<T>也定义了不同的变体,至多也可以传递16种不同的参数类型和一个返回类型。

  • Func<out TResult>:调用带返回类型且无参数方法
  • Func<int T,out TResult>:调用带一个参数的方法

示例使用Func<Int T,out TResult>委托

delegate double DoubleOp(double x);Func<double,double>[] operations = {MathOperations.MultipleByTwo,MathOperations.Square};

那么调用时方法应为

private static void ProcessAndDisplayNumber(Func<double, double> action, double value)
{double result = action(value);Console.WriteLine("Value is {0},result of operation is {1}", value, result);
}

冒泡排序案例体会委托的用途:

/// <summary>
/// 冒泡排序的泛型版本
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sortArray"></param>
/// <param name="comparison"></param>
static public void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)
{bool swapped = true;do{swapped = false;for (int i = 0; i < sortArray.Count - 1; i++){if (comparison(sortArray[i + 1], sortArray[i])){T temp = sortArray[i];sortArray[i] = sortArray[i + 1];sortArray[i + 1] = temp;swapped = true;}}} while (swapped);
}

参数2实则委托,定义了函数签名,即包含连个参数且返回值为布尔类型,本质上传递的是一个函数指针。

5.多播委托

定义:包含多个方法的委托,可按顺序连续调用多个方法,为此委托的签名就必须返回void,否则只能得到委托调用的最后一个方法的结果。

可以使用返回类型为void的Action<T1>委托:

Action<double> operations = MathOperations.MultiplyByTwo;
operations += MathOperations.Square;

多播委托可以识别运算符“+”和“+=”,还识别运算符“-”和“-=”,以从委托中删除方法调用。

注意:

  • 多播委托对调用方法链的顺序并未正式定义,因此应避免编写依赖于以特定顺序调用方法的代码。
  • 多播委托调用的其中一个方法抛出异常,整个迭代就会停止。

为了避免第二个问题应自己迭代方法列表,如:

static void Main()
{Action d1 = One;d1 += Two;Delegate[] delegates = d1.GetInvocationList();//返回委托数组foreach(Action d in delegates){try{d();}catch(Exception){Console.WriteLine("Exception caught");}}
}

6.匿名方法

另外一种使用委托的方法,是用作委托的参数的一段代码。

string mid = ",middle part,";Func<string,string> anonDel = delegate(string param)
{param += mid;param += " and this was added to the string.";return param;
};
Console.WriteLine(anonDel("Start of string"));

匿名方法的优点:减少代码,不必定义由委托使用的方法,有助于降低代码的复杂性,特别是定义事件时。

匿名方法的缺点:代码执行的不太快,编译器仍定义了一个方法,只是名称是自动指定的。当需多次编写功能相同的方法,则不建议使用。

必须遵循的规则:

  • 在匿名方法中不能使用跳转语句(break、goto或continue)跳到该匿名方法的外部,反之亦然:匿名方法外部的跳转语句不能跳到该方法内部。
  • 在匿名方法内部不能访问不安全的代码。也不能访问在匿名方法外部使用的ref和out参数,但可以访问方法外部定义的其他变量。

7.Lambda表达式

从C#3.0开始,可以使用Lambda替代匿名方法。

将上面示例修改为Lambda语法:

string mid = ",middle part,";Func<string,string> anonDel = param =>
{param += mid;param += " and this was added to the string.";return param;
};
Console.WriteLine(anonDel("Start of string"));

单个参数和多个参数定义方式:

Func<string,string> oneParam = s => String.Format("change uppercase {0}",s.ToUpper());//单参

Func<double,double,double> TwoParams = (x,y) => x*y;//多参

Func<double,double,double> twoParamsWithTypes = (double x,double y) => x*y;//添加参数类型

注意:如果Lambda表达式只有一条语句,在方法块内就不需要花括号和return语句,因为编译器会添加一条隐式的return语句。

Lambda表达式外部的变量:

通过Lambda表达式可以访问Lambda表达式外部的变量,但是要比较小心,特别是通过另一个线程调用Lambda时,我们可能不知道进行了这个调用,也不知道当前外部变量的值是什么。

编译器其实在定义Lambda表达式时创建了一个匿名类,外部变量通过构造函数传递进来。

8.事件

事件基于委托,为委托提供一种发布/订阅机制。

namespace Wrox.ProcSharp.Delegates
{//事件发布程序public class CarInfoEventArgs : EventArgs{public CarInfoEventArgs(string car){this.Car = car;}public string Car{get;private set;}}public class CarDealer{public event EventHandler<CarInfoEventArgs> NewCarInfo;public void NewCar(string car){Console.WriteLine("CarDealer, new car{0}", car);if(NewCarInfo != null){NewCarInfo(this, new CarInfoEventArgs(car));}}}//事件监听器public class Consumer{private string name;public Consumer(string name){this.name = name;}public void NewCarIsHere(object sender,CarInfoEventArgs e){Console.WriteLine("{0}:car{1} is new",name,e.Car);}}//订阅class Program{static void Main(){var dealer = new CarDealer();    var michael = new Consumer("Michael");dealer.NewCarInfo += michael.NewCarIsHere;dealer.NewCar("Mercedes");}}
}

View Code

作为一个约定,事件一般使用带两个参数的方法:

  • 第一个参数是一个对象,包含事件的发送者
  • 第二个参数提供了事件的相关信息,随不同的事件类型而不同。

对于EventHandler<TEventArgs>,第一个参数是object类型,第二个参数是T类型,还有一个关于T的约束:必须派生自基类EventArgs

public event EventHandle<TEventArgs>(object sender,TEventArgs e) where TEventArgs:EventArgs

定义事件的简化记法,编译器自动创建委托变量

public event EventHandler<CarInfoEventArgs> NewCarInfo;

完整记法

private delegate EventHandler<CarInfoEventArgs> newCarInfo;
public event EventHandler<CarInfoEventArgs> NewCarInfo
{add{newCarInfo += value;}remove{newCarInfo = value;}
}

与多播委托一样,方法调用顺序无法保证,如果要控制调用,需使用Delegate类的GetInvocationList()方法显式调用。

8.1弱事件

转载于:https://www.cnblogs.com/KevinG/p/3581690.html

C#高编 - 委托、Lambda表达式和事件相关推荐

  1. 委托、Lambda表达式和事件

    1. 引用方法 委托是寻址方法的.NET版本.在C++中,函数指针只不过是一个指向内存位置的指针,它不是类型安全的.我们无法判断这个指针实际指向什么,像参数和返回类型等项就更无从知晓了.而.NET委托 ...

  2. 委托、Lambda表达式、事件系列07,使用EventHandler委托

    谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用 ...

  3. 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别...

    在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...

  4. Kotlin学习笔记 第三章 函数 高阶函数 lambda表达式 内联函数

    参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...

  5. 【C# 委托 Lambda表达式】一个简单的例子

    委托 委托类似于C++函数指针,但委托是完全是面向对象的,是安全的数据类型. 委托允许将方法作为参数进行传递. 运行结果 j=25 代码 using System; using System.Coll ...

  6. 高阶函数-lambda表达式

    #2.6 map()# 第一个参数传入一个函数,,第二个参数为一个可迭代对象li_1 = (1,3,5,7)def funcA(x): return x*xm1 = map(funcA,li_1)pr ...

  7. java里函数式表达式_java8入门(lambda表达式、函数式接口相关)

    1.java8介绍 (1)概念 是Java语言历史上变化最大的一个版本,其调整java编程向函数式风格迈进,不会对老版本产生影响 (2)优势 速度鞥更快 永久帯取消 HashMap的优化 Concur ...

  8. Java8中Lambda表达式的10个例子

    Java8中Lambda表达式的10个例子  例1 用Lambda表达式实现Runnable接口 Java代码   //Before Java 8: new Thread(new Runnable() ...

  9. 匿名函数,Lambda表达式,委托

    C# 委托(Delegate) C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针.委托(Delegate) 是存有对某个方法的引用的一种引用类型变量.引用可在运行时被改变.委托( ...

  10. delegate、Lambda表达式、Func委托和Expression(TDelegate)表达式目录树

    1.delegate MSDN:一种安全地封装方法的类型,它与 C 和 C++ 中的函数指针类似.与 C 中的函数指针不同,委托是面向对象的.类型安全的和保险的.委托的类型由委托的名称定义 class ...

最新文章

  1. GridView和DetailsView在同一页与不同页两种情况的联动
  2. rtmp官方协议详解
  3. HTML5学习笔记简明版(4):新元素之video,audio,meter,datalist,keygen,output
  4. C++标准库 第七章 STL迭代器
  5. 后缀表达式的值(信息学奥赛一本通-T1331)
  6. InputStreamReader和 OutputStreamWriter
  7. 华为公开“实现汽车中电子控制功能的系统”相关专利
  8. 两个分数化简比怎么化_怎么化行最简形矩阵?
  9. HDU 1176 免费馅饼(记忆化搜索)
  10. tomcat是什么_为什么开发者放弃了Tomcat,选择了Undertow?
  11. SpringMVC 统一异常处理
  12. Oracle 10g安装图解教程
  13. 数据库和数据库管理系统的区别
  14. R语言---相关系数
  15. select()函数
  16. 【web实战-业务逻辑】评论点赞逻辑
  17. 清除浏览器js和css缓存
  18. iptables限制Docker IP和端口访问
  19. Android 扬声器与听筒的切换
  20. iOS14iCloud云盘为何删除文稿和数据不好用_5个特征选择算法,让你的数据处理得心应手...

热门文章

  1. Linux系统编程 -- volatile关键字
  2. JVM学习04-垃圾回收概念与算法
  3. 编写二分查找和使用集合类的二分查找实现
  4. 跨域问题:Access-Control-Allow-Origin
  5. Java I/O系统之Object流
  6. pku 3533 Light Switching Game(nim 积)
  7. Texar安装、Textgenrnn安装
  8. wiki——Isogeometric_analysis
  9. Matlab协方差矩阵的计算原理
  10. bzoj1396: 识别子串