引用方法
委托是寻址方法的.NET版本。
在C++中,函数指针只不过是一个指向内存位置的指针,它不是类型安全的。
我们无法判断这个指针指向声明,参数和返回类型就更无从可知了。
而.NET的委托完全不同,委托是安全类型,它定义了返回类型和参数的类型。
委托不仅包含对方法的引用,也可以包括对多个方法的引用。
lambda表达式与委托直接相关,当委托是参数类型时,就可以用lambda表达式实现委托的引用方法。
委托
当要把方法传递给其他方法时,就需要使用委托。
int i = int.Parse("777");//将字符串777转换为数字777
上面的示例是将数据作为参数传递给方法,
如果要将方法传递给另一个方法需要将方法作为参数进行传递。
C#是面向对象编程的,几乎没有方法是孤立的存在,在调用方法前通常需要与类实例相关联。
所以在C#中,如果要传递方法,就必须将方法的细节封装到一种新的对象类型中,
即委托,委托是一种特殊类型的对象,其特殊之处在于委托只包含一个或多个方法的地址,而不包含数据。
声明委托
在C#中使用类时,通常需要先定义这个类,然后实例化该类的一个对象,除非类中只包含静态方法。
对于委托,同样需要先定义,对于委托定义就是告诉编译器这种类型的委托表示哪种类型的方法,然后必须创建该委托的一个或多个实例。编译器在后台将创建表示该委托的一个类。
声明委托使用关键字 delegate 。
delegate void X(int x);
上面的示例声明了一个委托X,并指定该委托的每个实例都可以包含一个方法的引用,该方法带有 int参数,并返回void。
在定义委托时必须给出它所表示的方法的签名(所有参数类型和方法名)和返回类型的等全部细节。
假定定义一个委托 ,该委托表示的方法有两个long型参数,返回类型为double。
delegate double X(long x1,long x2);
或要定义一个委托,表示的方法不带参数,返回一个string类型
delegate string Str();
其语法类似于方法的定义,但是没有方法主体,且定义的前面要加上关键字 delegate .
因为定义委托基本上就是定义一个新类(在定义委托后,编译器其会创建表示该委托的一个类),
所以可以在定义类的任何相同地方定义委托。
可以在类的内部定义,也可以在类的外部定义,还可以在名称空间中把委托定义为顶层对象。
可以在委托上添加访问修饰符:public,private, protected等。
public delegate string Str();
使用委托
using System;
using static System.Console;
namespace ConsoleApp20
{
class Program
{
public delegate string Str();
static void Main(string[] args)
{
int i = 777;//整形数字777
WriteLine(i.ToString() + "asdw");//输出i的字符串格式,进行字符串拼接判断
Str a = new Str(i.ToString);//对委托实例化,传入方法名称
WriteLine(a() + "qwer");//使用委托,
ReadKey();
}
}
}
在C#中,委托的实例化总是接受一个参数的构造函数,这个参数就是委托的引用方法,这个方法必须匹配最初定义委托时的签名。这里tostring返回字符串,委托返回字符串。
在任何代码中,使用都需要提供委托实例的名称,后面的括号内应该包含调用该委托中的方法时需要使用的任何等效参数。 a() ,a是委托的实例名称,括号为tostring需要的参数。
为了减少输入量,在需要委托实例的每个位置可以只传送地址的名称,这称为 委托推断 ,只要编译器可以把委托实例解析为特定的类型,这个C#特性就是有效的。
public delegate string Str();
Str a = i.ToString;
Str b = new Str(i.ToString);
编译器会检查a需要的委托类型,因此会创建一个Str委托类型的一个实例。用对象i将方法的地址传给构造函数。
注意,为什么这里的 ToString 不添加括号,输入括号会调用方法,而调用tostring方法会返回一个不能被赋予委托变量的字符串对象。
委托推断可以用于需要委托实例的任何地方使用,委托推断也可以用于事件,因为事件基于委托。
委托的一个特征是类型安全,可以确保被调用的签名是正确的,
委托不关心什么类型的对象上调用该方法,甚至不考虑方法是静态方法还是实例方法。
给定委托的实例可以引用任何类型的任意对象上的实例方法或静态方法,只要方法的签名匹配委托的签名即可。
简单的委托示例
定义一个类包含两个静态方法,翻倍和平方
public class MyMath
{
public static int Doubling(int x)//翻倍
{
return x * 2;
}
public static int Square(int x)//平方
{
return x * x;
}
}
然后声明委托
public delegate int DMyMath(int x);
对委托实例化
DMyMath[] s = { MyMath.Doubling, MyMath.Square };
调用委托
using System;
using static System.Console;
namespace ConsoleApp20
{
class Program
{
public class MyMath//自定义的数学计算类
{
public static int Doubling(int x)//静态方法翻倍
{
return x * 2;
}
public static int Square(int x)//静态方法平方
{
return x * x;
}
}
public delegate int DMyMath(int x);//声明委托
static void F(DMyMath s, int x)//声明一个静态方法,参数为委托实例,整形数字
{
int result = s(x);//调用委托
WriteLine(result);
}
static void Main(string[] args)
{
DMyMath[] s = { MyMath.Doubling, MyMath.Square };//委托推断生成委托的实例化
WriteLine(s[0](10));//20 调用委托方法,参数放在括号内
WriteLine(s[1](7));//49
ReadKey();
}
}
}
这里实例化一个委托实例s数组,该数组的每个元素都初始化为指向MyMath类实现的不同操作。(一旦定义了委托类,基本上既可以实例化它的实例,就像处理一般类那样,所以把委托的实例放在数组中是可行的)
s[0]表示委托数组的第一个方法。
s0表示实际上调用这个方法,参数放在括号内。
特殊的,将委托实例作为参数传入方法 F ,在方法中调用委托,将返回结果存储在result。
Action和Func委托
除了为参数和返回类型定义一个新委托类型之外,还可以使用Action
泛型 Action
没有泛型参数的 Action 类可以调用没有参数的方法, Action