C#的委托,类似C++里的函数指针,相当于是给函数取了一个别名,其实质上是一种引用类型,使用前需要先创建一个委托对象;一个委托对象可以持有一个或多个方法的引用,持有的方法可以是实例方法,也可以是静态方法。
系统提供的委托类型
Action
Action是一种至少0个参数、无返回值的泛型委托:
//格式:
Action act = new Action(对象名.方法名); //实例化对象
act.Invoke(); //调用(Invoke可省略,即写成:act();)
//举个例子,假设此时存在一个类Test(),其中有这样一个方法:
public void MyMethod()
{
Console.WriteLine("Hello World!");
}
//在Main方法中调用:
static void Main(string[] args)
{
Test t = new Test();
Action act = new Action(t.MyMethod);
act(); //像调用方法一样去使用委托即可
}
//运行结果为:
Hello World!
Func
Func是一种至少0个参数、至多16个参数、必须有返回值的泛型委托(注意返回值类型要写在最后)
//格式
Func<参数类型, 返回值类型> func = new Func<参数类型, 返回值类型>(对象名.方法名); //实例化对象
func(参数列表); //调用
//举个栗子,假设此时存在一个类Test(),其中有这样一个方法:
public double MyMethod(int a,int b)
{
return a + b;
}
//在Main方法中调用:
static void Main(string[] args)
{
Test t = new Test();
Func<int, int, double> func = new Func<int, int, double>(t.MyMethod);
Console.WriteLine("4+7="+func(4, 7));
}
//运行结果为:
4+7=11
自定义的委托类型
声明与调用
我们也可以自己定义一个委托类型,就像自己定义一个类一样,不过委托没有实现块。一般我们会将其声明在类之外(要声明在类里面也行,但是不推荐这么做),这里我依然使用一个例子来说明自定义委托的声明与调用,并与之目标方法做一个对比:
//目标方法的声明:
public double MyAdd(int x,int y)
{
return x + y;
}
//自定义委托的声明:
public delegate double DelAdd(int x, int y); //对比一下两个声明,声明委托的格式和声明方法很相似,就是在其目标方法的返回值类型前加个“delegate”,且没有方法主体。
//在Main方法中调用:
static void Main(string[] args)
{
Test t = new Test();
DelAdd delAdd = new DelAdd(t.MyAdd); //实例化委托对象
Console.WriteLine("4+7=" + delAdd(4, 7));
}
//运行结果为:
4+7=11
在创建委托对象时,还有一种快捷语法,即不带new运算符:
DelAdd delAdd = t.MyAdd; //与前面的 “DelAdd delAdd = new DelAdd(t.MyAdd);” 等价
这种形式看上去更像是在赋值,将方法的地址赋值给委托变量;这种快捷语法之所以能够工作是因为在方法名称和其相应的委托类型之间存在隐式转换。
重新赋值
由于委托是引用类型,我们可以通过给委托变量重新赋值来改变其引用的方法;不过委托对象是不变的,即委托对象与目标方法是绑定的,所以当我们重新赋值时,内存中会创建新的委托对象,委托变量会指向新的委托对象,而旧的委托对象会被垃圾回收器回收。
//方法1:
public void MyMethod1()
{
Console.WriteLine("方法1");
}
//方法2:
public void MyMethod2()
{
Console.WriteLine("方法2");
}
static void Main(string[] args)
{
Test t = new Test();
MyDelegate myDelegate = t.MyMethod1; //创建委托对象,引用方法1
myDelegate();
myDelegate = t.MyMethod2; //创建新的委托对象,引用方法2
myDelegate();
}
//运行结果:
方法1
方法2
组合委托
可以将两个委托组合成一个新的委托
//对上面的Main方法进行一些修改:
static void Main(string[] args)
{
Test t = new Test();
MyDelegate myDelegate1 = t.MyMethod1; //创建委托1号,引用方法1
MyDelegate myDelegate2 = t.MyMethod2; //创建委托2号,引用方法2
MyDelegate myDelegate3 = myDelegate1 + myDelegate2; // //创建委托3号,由前2个委托组合而成
myDelegate3(); //调用委托3号就相当于调用了委托1号和2号,即调用了方法1和方法2
}
//运行结果:
方法1
方法2
为委托添加方法
无返回值:
与组合委托相似,在调用一个委托时相当于调用了多个方法;除了把多个委托组合成一个委托外,我们还可以直接在一个委托上添加方法,如下代码所示:
static void Main(string[] args)
{
Test t = new Test();
MyDelegate myDelegate = t.MyMethod1; //创建一个委托,并引用方法1
myDelegate += t.MyMethod2; //为该委托再增加一个方法
myDelegate(); //调用此委托就相当于调用了方法1和方法2
}
//运行结果:
方法1
方法2
前面已经说过,委托是不变的,所以当我们使用 “+=” 运算符时,实际上是创建了一个新的委托对象。
有返回值:
当一个委托持有多个方法且每个方法都有返回值时,方法列表中的最后一个方法的返回值就是调用该委托返回的值:
//声明委托
public delegate int MyDelegate(int x, int y);
//声明方法
public int MyMethod1(int x,int y)
{
return x + y;
}
public int MyMethod2(int x, int y)
{
return x - y;
}
//调用
static void Main(string[] args)
{
Test t = new Test();
MyDelegate myDelegate = t.MyMethod1; //引用方法1
myDelegate += t.MyMethod2; //增加方法2,此时方法2是该委托的最后一个方法
Console.WriteLine(myDelegate(4,7)); //调用委托
}
//运行结果:-3
//分析:该委托对象持有方法1和方法2,先调用方法1,计算4+7,返回值11被忽略;再调用方法2,计算4-7,结果为-3;则该委托的返回值为-3,由方法2决定,与方法1无关。
带有引用参数:
当一个委托持有多个方法且每个方法都带有引用参数时,参数的值会受到列表中每个方法的影响,这很好理解:
//声明委托
public delegate void MyDelegate(ref int x);
//声明方法
public void MyMethod1(ref int x)
{
x += 4;
}
public void MyMethod2(ref int x)
{
x += 7;
}
//调用
static void Main(string[] args)
{
Test t = new Test();
MyDelegate myDelegate = t.MyMethod1; //引用方法1
myDelegate += t.MyMethod2; //增加方法2
int myVar = 10;
myDelegate(ref myVar);
Console.WriteLine("myVar的值为:"+myVar);
}
//运行结果: myVar的值为:21
从委托移除方法
与添加方法相似,我们也可以使用 “-=” 运算符将委托中的一个方法删去。
匿名方法
匿名方法是在实例化委托时内联声明的方法:
public delegate int MyDelegate(int x);
static void Main(string[] args)
{
Test t = new Test();
//在实例化委托的同时内联声明一个匿名方法:
MyDelegate myDelegate = delegate (int x)
{
return x + 5;
};
//调用委托:
Console.WriteLine(myDelegate(8));
}
//运行结果为:13
Lambda表达式:
我们可以使用Lambda表达式对匿名方法的声明进行简化,如上面的例子可以简化为:
MyDelegate myDelegate = (int x)=>
{
return x + 5;
};
//像这种只有一个return语句的匿名方法,还可以进一步简化:
MyDelegate myDelegate = x => x + 5;
//运算符 => 是一个Lambda运算符,读作“goes to”
params参数:
注意,当声明委托时,若参数列表里有params参数,匿名方法的参数列表必须省略params关键字。
//声明委托,带有params关键字:
public delegate void MyDelegate(int x, params int[] y);
//匿名方法的参数列表中不可以有params关键字:
MyDelegate myDelegate = delegate (int x, int [] y){};
—————————————————————————————————————————————
以上是我对委托这一块知识的一个小整理,希望对大家有帮助,有不当的地方还请大佬指正,谢谢!