什么是委托
- 委托是持有一个或多个方法的对象,执行委托类型的对象执行它“持有”的方法
- 如果我们要把方法当做参数来传递的话,就要用到委托。简单来说委托是一个类型,这个类型可以赋值一个方法的引用
- c#的委托可以看成c++的一个类型安全的、面向对象的c++函数指针
怎么创建委托
- 用关键字delegate,声明委托类型,返回值类型,方法的参数类型(类似我们声明一个类)
- 用已经声明的委托类型声明一个变量,变量持有对委托对象的引用
- 把同返回值和参数类型的方法,赋值给委托类型的变量,该变量持有对委托对象的引用
internal class Program
{
delegate void IntMethodInvoker(int i);
delegate void TwoLong(long a, long b);
delegate string GetAString();
static void Main(string[] args)
{
IntMethodInvoker intvoker = null;
TwoLong twoLong = null;
intvoker = test;
intvoker(10);
if(twoLong != null)
{
twoLong(2, 23);
}
int x = 123;
GetAString getAString = x.ToString;
GetAString getAString1 = new GetAString(x.ToString);
Console.WriteLine(getAString);
}
private static void test(int i)
{
Console.WriteLine("我是哈哈哈"+i);
}
}
委托与类的对比
|
类 |
委托 |
声明类型 |
声明类 |
声明委托(类型) |
声明类型 |
声明类类型的变量 |
声明委托类型的变量 |
实例化 |
创建类的实例并把它的引用赋值给变量 |
创建委托的实例并把它的引用赋值给变量,然后增加第一个方法 |
使用变量 |
使用类对象 |
调用委托对象 |
进一步理解委托
- 委托持有的方法可以是任何类或结构的方法,可以是静态方法,实例化对象的方法
- 委托的参数类型可以包括ref和out修饰符
- 委托持有方法的列表称为调用列表
- 调用委托就会执行调用列表的所有方法
拆解
声明委托类型
delegate void Mydel(int x);
- 委托类型为Mydel,可以被委托的方法返回值必须是void,参数类型必须是int
- 与方法声明不同,在 C# 中,方法声明必须在类或结构体内部,而委托类型声明不需要在类内部声明
创建委托对象
1. 通过new创建对象
MyDel delVar;
delVar = new MyDel(myInstObj.MyM1);
2.可以通过方法名称和委托类型之间的隐式转换
delVar = myInstObj.MyM1;
- 委托是引用类型,所以要有引用和对象
- 赋值给委托类型的引用的方法,返回值和签名必须与声明委托类型时候保持一致
- 创建委托对象时候,会把第一个方法放入委托的调用列表
- 委托的实例本身是引用类型,因此委托实例存储在堆上。而委托所引用的方法则可能存储在堆上或者栈上,具体取决于方法的生存期。
- 委托的实例是一个引用类型的实例(而不是值类型),那么该方法会存储在堆上,引用的地址是堆里的地址
- 如果委托引用的方法是一个静态方法,或者是一个实例方法,但委托的实例是一个值类型的实例,那么该方法可能会存储在栈上。这是因为值类型的实例通常存储在栈上,而静态方法则不需要与特定的对象实例关联。
组合委托
- 委托是持有一个或多个方法的引用
- 可以给委托添加方法+=,也可以移除方法-=
为委托添加方法
MyDel delVar = inst.MyM1;
delVar += SC1.m3;
delVar += X.Act;
从委托移除方法
delVar -= SC1.m3;
- -=会从列表最后开始查找,移除第一个与方法匹配的实例
委托是恒定的
- 委托和字符串类似,都是不可变的,委托对象被创建之后不能再被改变
- 在添加方法时候,实际会在堆里新创建一个新委托,把新委托赋值给引用变量,这样旧委托没有被引用,等待GC回收内存
- 同理,移除也是创建新委托
调用委托
1. 与调用方法一样调用委托
MyDel delVar = inst.MyM1
delVar += SC1.m3;
delVar += X.Act;
if(delVar!=null){
delVar(55);
}
- 调用空委托会抛出异常,所以在调用委托时候,要先判断委托的调用列表是否为空,调用列表为空,则委托类型的引用==null
- 调用委托后,会调用调用列表的所有方法,先添加的方法先调用
- 会把55参数会作用于调用列表的每一个方法(除非一个参数是输出参数)
2. 使用Invoke和空条件运算符调用
delVar?.Invoke(65);
示例</