1.1 引用方法
委托是寻址方法的 .NET 版本。委托是类型安全的类。它定义了返回类型和参数的类型。委托类不仅包含对方法的引用,也可以包含对多个方法的引用。
Lambda 表达式与委托直接相关。当参数是委托类型时,就可以使用Lambda表达式实现委托引用的方法。
1.2委托
当要把 方法 传送给其他方法时,需要使用 委托。以下两个示例:
- 启动线程和任务——在C# 中,可以告诉计算机并行运行某些新的执行序列同时运行当前的任务。这种序列就是称为线程,在其中一个基类 System.Threading.Thread 的一个实例上使用方法 Start(),就可以启动一个线程。如果要告诉俺计算机启动一个新的执行序列,就必须说明要在哪里启动该序列。必须为计算机提供开始启动的方法的细节,即Thread类的构造函数必须带有一个参数,该参数定义了线程调用的方法。
- 事件——一般是通知代码发生了什么事件。GUI 编程主要处理事件,在引发事件时,运行库需要知道应执行哪个方法。这就需要把处理事件的方法作为一个参数传递给委托的。
委托只是一种特殊类型的对象,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托包含的只是一个或多个方法的地址。
1.2.1 声明委托
在C#中使用一个类时,分两个阶段。首先,需要定义这个类,即告诉编译器这个类由什么字段和方法组成。然后(除非只使用静态方法),实例化类的一个对象。使用委托时,也需要经过这两个步骤。首先必须定义要使用的委托,对于委托,定义它就是告诉编译器这种类型的委托表示哪种类型的方法,然后,必须创建该委托的一个或多个实例。编译器在后台将创建表示该委托的一个类。
定义委托的语法如下:
delegate void IntMethodInvoker(int x);
上面示例中,定义了一个委托IntMethodInvoker,并制定该委托的每个实例都可以包含一个方法的引用,该方法带有一个int 参数,并返回void。理解委托的一个要点是它们的类型安全性非常高。在定义委托时,必须给出它所表示的方法的 签名和返回类型 等全部细节。
例:delegate double TwoLongsOp(long first,long second);
或 delegate string GetAString();
委托的语法类似于方法的定义,但没有方法体,定义的前面要加上关键字 delegate。因为定义委托基本上定义一个新类,所以可以在定义类的任何相同地方定义委托,也就是说,可以在另一个类的内部定义,也可以在任何类的外部定义,还可以在名称空间中把委托定义为 顶层对象。根据定义的可见性,和委托的作用域。可以在委托的定义上应用任意常见的访问修饰符。public、 private、 protected等。
委托实现为派生自基类System.MulticastDelegate 的类,System.MulticastDelegate 又派生自基类System.Delegate。
在创建委托时,所创建的委托的实例仍称为委托。
1.2.2 使用委托
下面的例子来说明使用委托:
private delegate string GetAString();
static void Main()
{
int x=40;
GetAString firstStringMethod=new GetAString(x.ToString());
Console.WriteLine("String is {0}",firstStringMethod());
}
或
using System;
namespace DelegateTest
{
class Program
{
private delegate string GetAString();
static void Main(string[] args)
{
int x = 40;
GetAString fisrtMethod = new GetAString(new Program().SayHi);
Console.WriteLine("String is {0}",fisrtMethod());
//与 Console.WriteLine("String is {0}",firstMethod.Invoke());
}
private string SayHi()//必须匹配定义委托时的签名。
{
return "Say hi!";
}
}
}
//结果为:String is Say hi!
在上面的代码中,实例化了类型为GetAString 的一个委托,并对它进行初始化,使它引用整形变量 x 的ToString() 方法。在C#中,委托在语法上总是接受一个参数的构造函数,这个参数就是委托引用的方法。这个方法必须匹配最初定义委托时的签名。所以在这个示例中,如果用不带参数并返回一个字符串的方法来初始化 firstStringMethod 变量,就会产生一个编译错误。
实际上,给委托实例提供圆括号与调用委托类的 Invoke() 方法完全相同。因为 firstStringMethod 是委托类型的一个变量,所以C# 编译器会用 firstStringMethod.Invoke() 代替 firstString()。
firstStringMethod()----->firstStringMethod.Invoke();
委托推断:只需要委托实例,就可以只传送地址的名称。委托推断可以在需要委托实例的任何地方使用。委托推断也可以用于事件,因为事件基于委托。
☆:调用上述方法名时输入形式不能为 x.ToString()(不要输入圆括号),也不能把它传送给委托变量。输入圆括号调用一个方法。调用 x.ToString() 方法会返回一个不能赋予委托变量的字符串对象。只能把方法的地址赋予委托变量。
***委托的一个特征是它们的类型时安全的,可以确保被调用的方法的签名是正确的,但有趣的是,它们不关心在什么类型的对象上调用该方法,甚至不考虑该方法时静态方法,还是实例方法。
***给定委托的实例可以引用任何类型的任何对象上的实例方法或静态方法——只要方法的签名匹配于委托的签名即可。
实例:
using System;
namespace DelegateTest
{
class Program
{
delegate double DoubleOp(double x);//定义委托
static void Main(string[] args)
{
DoubleOp[] operations = { MathOperations.MultiplyByTwo,MathOperations.Square};
for(int i = 0; i < operations.Length; i++)
{
Console.WriteLine("Using Operations[{0}]:",i);
Console.WriteLine(operations[i]);
ProcessAndDisplayNumber(operations[i], 3.0);
ProcessAndDisplayNumber(operations[i], 7.32);
ProcessAndDisplayNumber(operations[i], 16.23);
Console.WriteLine();
}
}
static void ProcessAndDisplayNumber(DoubleOp actions, double value)
{
double result = actions(value);
Console.WriteLine("Value is {0},result of operation is {1}", value, result);
}
}
}
1.2.3 Action<T> 和 Func<T> 委托
泛型 Ation<T> 委托表示引用一个 void 返回类型的方法。这个委托类存在不同的变体,可以传递至多16种不同的参数类型。没有泛型参数的Action的类可以调用没有参数的方法。
Action<in T> 或 Action< in T1, in T2>
Func<T> 委托可以以类似的方式使用。Func<T>允许调用带返回类型的方法。与Action<T> 类似,Func<T> 也定义了不同的变体,至多也可以传递16个参数类型和一个返回类型。
Func<in T,out TResult> 或 Func<in T1,in T2,in T3,in T4,out TResult>
上节的 delegate double DoubleOp(double x); 也可以使用 Func<in T, out TResult>委托。
如下所示:
Func<double,double>
![]()
operations={MathOperations.MultiplyByTwo,MathOperations.Square};
调用过程如下
static void ProcessAndDisplayNumber(Func<double,double> action,double value)
{
double result=action(value);
Console.WriteLine("Value is {0},result of operations is {1}",value ,result);
}
冒泡排序示例:(BubbleSorter示例)
using System;
using System.Collections.Generic;
namespace DelegateBubbleSort
{
class BubbleSort
{
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);
}
}
}
using System;
namespace DelegateBubbleSort
{
class Employee
{
public Employee()
{
}
public Employee(string name,decimal salary)
{
this.Name = name;
this.Salary = salary;
}
public string Name
{
get;
private set;
}
public decimal Salary
{
get;
private set;
}
public override string ToString()
{
return String.Format("{0},{1:C}", Name, Salary);
}
public static bool CompareSalary(Employee e1,Employee e2)
{
return e1.Salary < e2.Salary;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DelegateBubbleSort
{
class Program
{
static void Main(string[] args)
{
Employee[] empolyee =
{
new Employee("Bug",1000),
new Employee("PSD",10000),
new Employee("SSSD",1500)
};
BubbleSort.Sort(empolyee, Employee.CompareSalary);
foreach (var emp in empolyee)
{
Console.WriteLine(emp);
}
}
}
}
1.2.4 多播委托
前面使用的每个委托都只包含一个方法调用。调用委托的次数与调用方法的次数相同。如果要调用多个方法,就需要多次显式调用这个委托。但是,委托也可以包含多个方法,这种委托称为多播委托。
如果调用多播委托,就可以按顺序连续调用多个方法,为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果。
【 通过一个委托调用多个方法还可能导致一个大问题。多播委托包含一个逐个调用的委托集合,如果通过委托调用的其中一个方法抛出一个异常,整个迭代就会停止。】
using System;
namespace MutiplyDelegate
{
class Program
{
static void One()
{
Console.WriteLine("One!");
throw new Exception("Error in One!");
}
static void Two()
{
Console.WriteLine("Two!");
}
static void Main(string[] args)
{
Action dl = One;
dl += Two;
try
{
dl();
}
catch (Exception )
{
Console.WriteLine("I caught you!");
}
}
}
}
//结果
One!
I caught you!
上面例子中,委托只调用了第一个方法,因为第一个方法抛出了一个异常。所以委托的迭代会停止,不再调用Two()方法。没有指定调用方法的顺序时,结果会有所不同。
在这种情况下,为了避免这个问题,应自己迭代方法列表。 Delegate类定义 GetInvocationList()方法,它返回一个Delegate 对象数组。源码如下:
using System;
namespace MutiplyDelegate
{
class Program
{
static void One()
{
Console.WriteLine("One!");
throw new Exception("Error in One!");
}
static void Two()
{
Console.WriteLine("Two!");
}
static void Main(string[] args)
{
//Action dl = One;
//dl += Two;
//try
//{
// dl();
//}
//catch (Exception )
//{
// Console.WriteLine("I caught you!");
//}
Action dl = One;
dl += Two;
Delegate[] delegates = dl.GetInvocationList();
foreach (Action d in delegates)
{
try
{
d();
}
catch (System.Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
}
//结果
One!
Error in One!
Two!
1.2.5匿名方法
通过匿名方法是用作委托的参数的一段代码。
用匿名方法定义委托的语法与前面的定义并没有区别。但在实例化时,就有区别了。
using System;
namespace MutiplyDelegate
{
class Program
{
static void Main(string[] args)
{
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("Strat of !"));
}
}
}
结果:
Strat of !, middle part, and this was added to the string .
Func<string,string> 委托接受一个字符串参数,返回一个字符串。anonDel 是这种委托类型的变量。不是把方法名赋予这个变量,而是使用一段简单的代码: 它前面是关键字 delegate ,后面是一个字符串参数。在调用委托时,把一个字符串作为参数传递,将返回的字符串输出到控制台上。
【在使用匿名方法时,必须遵循两条规则。
- 在匿名方法中不能使用跳转语句(break,goto,continue)跳到该匿名方法的外部,反之亦然:匿名方法外部的跳转语句不能呢个跳到匿名方法的内部。
- 在匿名方法内部不能访问不安全的代码。另外也不能访问在匿名方法外部使用的 ref 和 out参数 。但可以使用在匿名方法外部定义的其他变量】
如果需要用匿名方法多次编写同一个功能,就不要使用匿名方法。
- C#编程 委托 Lambda表达式和事件
委托 如果我们要把方法当做参数来传递的话,就要用到委托.简单来说委托是一个类型,这个类型可以赋值一个方法的引用. 声明委托 在C#中使用一个类分两个阶段,首选定义这个类,告诉编译器这个类由什么字段和方 ...
- C#学习笔记三(委托·lambda表达式和事件,字符串和正则表达式,集合,特殊的集合)
委托和事件的区别 序号 区别 委托 事件 1 是否可以使用=来赋值 是 否 2 是否可以在类外部进行调用 是 否 3 是否是一个类型 是 否,事件修饰的是一个对象 public delegate vo ...
- 委托、Lambda表达式、事件系列07,使用EventHandler委托
谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用 ...
- 委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别
在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...
- 委托、Lambda表达式、事件系列05,Action委托与闭包
来看使用Action委托的一个实例: static void Main(string[] args) { int i = 0; Action a = () => i++; a(); a(); C ...
- 委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理
委托是多播委托,我们可以通过"+="把多个方法赋给委托变量,这样就形成了一个委托链.本篇的话题包括:委托链是怎样形成的,如何调用委托链方法,以及委托链异常处理. □ 调用返回类型为 ...
- 委托、Lambda表达式、事件系列03,从委托到Lamda表达式
在"委托.Lambda表达式.事件系列02,什么时候该用委托"一文中,使用委托让代码简洁了不少. namespace ConsoleApplication2 { internal ...
- 委托、Lambda表达式、事件系列02,什么时候该用委托
假设要找出整型集合中小于5的数. static void Main(string[] args) { IEnumerable<int> source = new List<int&g ...
- 委托、Lambda表达式、事件系列01,委托是什么,委托的基本用法,委托的Method和Target属性
委托是一个类. namespace ConsoleApplication1 { internal delegate void MyDelegate(int val); class Program { ...
- C#高级编程(第9版) 第08章 委托、lambda表达式和事件 笔记
本章代码分为以下几个主要的示例文件: 1. 简单委托 2. 冒泡排序 3. lambda表达式 4. 事件示例 5. 弱事件 引用方法 委托是寻址方法的.NET版本.在C++中函数 ...
随机推荐
- flex自适应高度内容高度超出容器高度自动出现滚动条的问题
在容器中设置 flex-grow:2; overflow-y:auto;overflow-x:hidden;容器高度自适应. 内容高度不固定,无法出现滚动条,然后在容器中添加height:0,出现滚动 ...
- [LeetCode] Find Minimum in Rotated Sorted Array 寻找旋转有序数组的最小值
Suppose a sorted array is rotated at some pivot unknown to you beforehand. (i.e., 0 1 2 4 5 6 7 migh ...
- 调用altera IP核的仿真流程—上
调用altera IP核的仿真流程—上 在学习本节内容之后,请详细阅读<基于modelsim-SE的简单仿真流程>,因为本节是基于<基于modelsim-SE的简单仿真流程>的 ...
- 学写js Calender控件
好几个月没写博客了,一直在赶项目.项目现在终于处于稳定的状态,只是修修改改.作为后台程序员的我真是苦逼啊,从web到手机端接口我都得写,杂七杂八的事情...这两天终于闲下来了,没事儿看了一下关于js日 ...
- JavaScript面向对象的程序设计
ECMAScript支持面对对象(oo)编程,但不使用类或接口.对象可以在代码执行过程中创建和增强,因此具有动态性而非严格定义的实体.在没有类的情况下,可以此采用下列模式创建对象. 工厂模式,使用简单 ...
- NBUT 1457 莫队算法 离散化
Sona Time Limit:5000MS Memory Limit:65535KB 64bit IO Format: Submit Status Practice NBUT 145 ...
- thusc2016游记&&滚粗记&&酱油记
#include <cstdio> using namespace std; int main(){ puts("转载请注明出处:http://www.cnblogs.com/w ...
- iOS 打包 测试 发布
1.企业版 1.1 打包 1.1.1 使用apple企业账号 获取 证书cer,描述文件provision (开发 生产) *注: 描述文件 又 三者组成(cer + appId + bundleId ...
- mysql主从复制操作步骤
注: .做主从复制的两台服务器server-id不能相同 .主从的字符集要一样,不然数据导入会报错 .开启db01的log-bin功能 [root@db01 mysql]# vim /etc/my.c ...
- border:none 和border:0区别差异
border:none与border:0的区别体现为两点:一是理论上的性能差异,二是浏览器兼容性的差异. 性能差异: [border:0;]把border设为“0”像素效果等于border-width ...