C#学习笔记 -- 委托

委托

1、什么是委托

委托是持有一个或多个方法的对象, 委托与典型的对象不同, 可以执行委托, 这时候委托会执行它所持有的方法

2、概述

委托和类一样是一种用户定义类型, 但类表示的是数据和方法的集合, 而委托则持有一个或多个方法, 以及一系列预定义操作

可以把delegate看作一个包含有序方法列表的对象, 这些方法具有相同的签名和返回类型

  • 方法的列表称为调用列表

  • 委托持有的方法可以来自任何类或结构, 只要他们在如下方面匹配

    • 委托的返回类型

    • 委托的签名(包括ref和out修饰符)

  • 调用列表中的方法可以是实例方法也可以是静态方法

  • 在调用委托的时候, 会执行其调用列表所有方法

使用委托的步骤
  1. 声明一个委托类型. 声明委托与方法声明相似, 只是没有实现块

    delegate void MyDel(int val);
  2. 使用该委托类型声明一个委托变量

    MyDel del;
  3. 创建一个委托类型的对象, 并把它赋值给委托变量. 新的委托对象包含指向某个方法的引用, 这个方法的签名和返回类型必须跟第一步中定义的委托类型一样

    public class Printer
    {
        public void Print(int val)
        {
            Console.WriteLine($"val = {val}");
        }
    }
    Printer printer = new Printer();
    int val = 1;
    del = new MyDel{ printer.Print(val) };
  4. 可以选择为委托对象添加其他方法, 这些方法的签名和返回类型必须与第一步中定义的委托类型相同

  5. 可以像调用方法引用调用委托, 在调用委托的时候, 其包含的每个方法都会被执行

比较类和委托
  委托
声明类型 声明类 声明委托
声明类型的变量 声明类类型的变量 声明委托类型的变量
填充变量 创建类的实例并且把它的引用赋值给类型 创建委托的实例并且把它的引用赋值给变量, 然后增加第一个方法
使用变量 使用类对象 调用委托对象

3、声明委托

委托是类型, 返回类型和签名指定了委托接受的方法的形式

delegate 返回类型 委托类型名称(int x);
delegate void MyDel(int x);
注意 : 委托类型声明与方法不同如下
  1. 以delegate关键字开头

  2. 无方法主体

  3. 委托类型声明不需要在类内部声明, 因为他是类型声明

4、创建委托

有两种创建方式, 一种是使用new

  • 括号其中包含作为调用列表中第一个成员的方法的名称, 该方法可以是实例方法或静态方法

//实例方法
MyDel delVar = new MyDel(myInstObj.MyM1);
//静态方法
MyDel dVar = new MyDel(SClass.OtherM2);

一种是快捷语法, 仅由方法说明符构成

  • 这种语法是因为方法名称和其他相应的委托类型之间存在隐式转换

//实例方法
MyDel delVar = myInstObj.MyM1;
//静态方法
MyDel dVar = SClass.OtherM2;

除了为委托分配内存, 创建委托对象还会把第一个方法放入委托的调用列表

5、给委托赋值

由于委托是引用类型, 我们可以通过给他赋值来改变包括在委托变量中的引用, 旧的委托对象会被垃圾回收站回收

MyDel delVar = myIstObjc.MyM1;
MyDel delVar = SClass.OtherM2;

6、组合委托

MyDel delA = myIstObjc.MyM1;
MyDel delB = SClass.OtherM2;
MyDel delC = delA + delB;
  • 组合委托实际上并没有修改

  • 委托是恒定的, 委托对象被创建后不能再被改变

7、为委托添加方法

  • 使用+=符号添加方法

  • 在委托的调用列表“添加”了两个方法, 加在列表底部

  • 看上去可以为委托添加, 实际上是新建了一个委托, 委托指定组合, 再把新委托赋值给旧委托

  • 每次给委托添加方法, 都会在调用列表中新创建一个元素

MyDel delVar = myIst.MyM1;
delVar += SCl.M3;
delVar += X.Act;

8、为委托移除方法

  • 使用-=符号移除方法

  • 与委托添加方法一致

  • 如果调用列表中的方法有多个实例, -=符号从列表最后开始搜索, 并移除第一个与方法匹配的实例

  • 试图删除委托中不存在的方法将无效

  • 对空委托使用移除方法会出现异常, 可以通过将委托和null比较, 来判断为委托的调用列表是否为空,

9、调用委托

  • 在调用委托时, 他使用相同的参数来执行调用列表中的每一个方法

  • 可以通过两种方式调用委托, 一种像是调用方法一样调用委托, 另一种是使用委托的Invoke()

  • 如下方代码块, 可以将参数放在调用的圆括号内, 用于调用委托的参数作用与调用列表中的每一个方法(除非一个参数是输出参数)

    int val = 55;
    delVar(val);
  • 如果一个方法在调用列表中多次出现, 则在调用委托时, 每次在列表中遇到该方法时都会调用他

  • 调用时委托不能为空, 否则将引发异常, 可以使用if语句进行检查, 也可以使用空条件运算符和Invoke()

    int val = 55;
    if(delVar != null)
    {
        delVar(val);
    }
    //等价
    delVar?.Invoke(val);

10、调用空返回类型的委托

public delegate void PrintFunction();
class Printer
{
    public void Print1()
    {
        Console.WriteLine("Print1 -- instance");
    }
​
    public static void Print2()
    {
        Console.WriteLine("Pinter2 -- static");
    }
}
static void Main(string[] args)
{
    Printer printer = new Printer();
    //初始化委托
    PrintFunction pfDelegate = printer.Print1;
    //添加委托
    pfDelegate += Printer.Print2;
    pfDelegate += printer.Print1;
    pfDelegate += Printer.Print2;
​
    //执行委托
    //空条件运算符检查委托内是否为空
    pfDelegate?.Invoke();
​
    //使用if判断
    if (pfDelegate != null)
    {
        pfDelegate();
    }
    else
    {
        Console.WriteLine("empty");
    }
}

11、调用带返回值的委托

如果委托有返回值并且在调用列表中有一个以上的方法, 会发生如下情况

  • 调用列表中最后一个方法返回的值就是委托调用返回的值

  • 调用列表中其他方法的返回值都会被忽略

public delegate int MyDel1411();
class MyClass1411
{
    int intValue = 5;
    public int Add2() { intValue += 2; return intValue; }
    public int Add3() { intValue += 3; return intValue; }
}
static void Main(string[] args)
{
    MyClass1411 mc = new MyClass1411();
    MyDel1411 mDel = mc.Add2;
    mDel += mc.Add3;
    mDel += mc.Add2;
    Console.WriteLine($"Value: {mDel?.Invoke()}");
}

12、调用带引用参数的委托

  • 如果委托有引用参数, 参数值会根据调用列表的一个或多个方法的返回值而改变

  • 在调用委托列表的下一个方法时, 参数的新值(不是初始值)会传递给下一个方法

public delegate void MyDel1412(ref int x);
class MyClass1412
{
    public void Add2(ref int x) { x += 2; }
    public void Add3(ref int x) { x += 3; }
static void Main(string[] args)
{
    MyClass1412 mc = new MyClass1412();
    MyDel1412 mDel = mc.Add2;
    mDel += mc.Add3;
    mDel += mc.Add2;
​
    int x = 5;
    mDel?.Invoke(ref x);
​
    Console.WriteLine($"Value = x: {x}"); //12
}

posted on 2023-05-28 00:41  老菜农  阅读(36)  评论(0编辑  收藏  举报

导航