委托


委托

什么是委托

委托就是 “持有” 一组方法的对象,它类似于C++中的指针,只是C++的指针只能指向一个函数,但是委托可以指向多个函数,因为用委托类型定义的委托对象可以保存多个函数指针,相当于函数指针的升级版。

像Register注册机制和设计模式中的观察者模式和委托一样

一个简单委托的使用例子

void Main()
{
	MyClass obj = new MyClass();
	obj.ProcEvent();
}

namespace test
{
	class MyClass
	{
		delegate void PressBtn(int btnId); //1、声明一个委托类型
		
		public int derivedVal = 0; //字段
		public int Prop //属性
		{
			get => derivedVal;
			set => derivedVal = value;
		}
		
		//2、定义自己的函数
		private void ProcBtnPress(int id)
		{
			Console.WriteLine($"Buttton {id} is Pressed");
		}
		
		public void ProcEvent()
		{
			//3、声明一个委托对象,并把自己的函数传进去	
			PressBtn btnDelagete = new PressBtn(ProcBtnPress);
			
			//4、通过委托对象调用函数
			btnDelagete(10);
		}
		
	}//class
}//namespace

委托类型的声明

delegate关键字开头,后面跟一个函数的声明,有函数的返回值和签名(函数名和参数)。

这里的函数名即委托类型名,注意委托是引用类型。

delegate void PressBtn(int btnId); //1、声明一个委托类型

委托对象的创建

这里在创建委托对象的同时,传入了我们自定义的ProcBtnPress方法,这个方法会保存到委托对象的方法列表中,并且是第一个(因为在创建委托对象的时候就传入了)

//3、创建一个委托对象,并把自己的函数传进去	
PressBtn btnDelagete = new PressBtn(ProcBtnPress);

还可以先声明再保存引用

//声明一个委托对象
PressBtn btnDelagete;
//添加对方法的引用
btnDelagete = new PressBtn(ProcBtnPress);

//还可以直接这样赋值
//这种和上面的使用是等价的,因为在方法名称和相应的委托类型之间存在隐式转换
//PressBtn btnDelagete = ProcBtnPress;
btnDelagete = ProcBtnPress;

由于委托是引用类型,可以通过给他赋值来改变委托对象的引用

//第一个引用
btnDelagete = new PressBtn(ProcBtnPress1);

//第二个引用
//这里重新给委托变量赋值,第一个委托引用对象会被垃圾回收器回收
btnDelagete = new PressBtn(ProcBtnPress2);

添加委托方法

添加给委托对象的方法,它的返回值和签名一定要和定义的委托类型一致

delegate void PressBtn(int btnId); //1、声明一个委托类型

//2、定义自己的函数
private void ProcBtnPress(int id)
{
    Console.WriteLine($"Buttton {id} is Pressed");
}

添加给委托的方法即可以类的普通成员方法,也可以是静态方法

组合委托

将多个委托加成一个,

delegate void PressBtn(int btnId); //声明一个委托类型

void Main()
{
	MyClass1 obj = new MyClass1();
	PressBtn delegate1 = obj.ProcBtnPress;
	PressBtn delegate2 = MyClass2.ProcBtnPress;
	PressBtn delegate3 = delegate1 + delegate2;
	delegate3(20);
}

namespace test
{
	class MyClass1
	{
		public void ProcBtnPress(int id)
		{
			Console.WriteLine($"MyClass1 Buttton {id} is Pressed");
		}
	}//class

	class MyClass2
	{
		public static void ProcBtnPress(int id)
		{
			Console.WriteLine($"MyClass2 Buttton {id} is Pressed");
		}
	}//class
}//namespace

输出

MyClass1 Buttton 20 is Pressed
MyClass2 Buttton 20 is Pressed

给委托增加和移除方法

委托是恒定不变的,给委托增加和移除方法其实是创建了一个新的委托,新委托是旧委托的副本,只是调用列表变了。

1)增加方法

//使用+=运算符来给委托增加方法
delegate1 += obj.func1;

2)移除方法

移除会从列表的最后一个开始匹配,移除第一个匹配的方法

//使用1=运算符来移除方法
delegate1 -= obj.func1;
//如果不确定委托列表中是否有方法,可以用null来判断
//调用列表为空时,委托会为null
if (delegate1 != null)
{
    delegate1 -= obj.func1;
}

调用委托

正常调用时要判断委托列表是否为空,如果列表中没有方法就调用委托,则会报异常

1)像方法一样调用

if (delegate1 != null)
{
    delegate1(20);
}

2)使用委托的invoke方法来调用

//这里使用了一个空条件运算符来判断委托是否为空
delegate1?.Invoke(30);

调用带返回值的委托

带返回值的委托在调用时,返回的是最后一个方法的返回值,其他方法的返回值会被忽略

调用带引用参数的委托

如果方法参数含ref引用参数,那么该参数在第一个方法调用后,会被修改,参数修改后的值会传给下一个方法。