C#委托基础

C#中的委托

委托可以认为是持有一个或者多个方法的对象,委托可以认为是一个类型安全的、面向对象的C/C++函数指针。如下例程序:

namespace CSharpProject1;

delegate void MyDelegate(int value); // 声明委托类型(没有返回值的类型,使用delegate, value为委托参数)

class Program
{
   static  void PrintLow(int value)
    {
        Console.WriteLine($"{value} - Low Value");
    }

    static void PrintHigh(int value)
    {
        Console.WriteLine($"{value} - High Value");
    }
    
    static void Main(string[] args)
    {
        MyDelegate myDelegate;  //  声明委托变量
        // 创建随机数生成器
        Random r = new Random();
        int randomValue = r.Next(99);
        // 创建一个包含PrintLow或者PrintHigh的委托对象并赋值给委托变量
        myDelegate = (randomValue < 50) ? new MyDelegate(Program.PrintLow) : new MyDelegate(Program.PrintHigh);
        // 执行委托传递参数
        myDelegate.Invoke(randomValue); 
    }
}

委托和类一样,是一种用户定义类型,但类表示的是数据和方法的集合,二委托则持有一个或者多个方法。使用委托方法如下:

  1. 声明一个委托类型。委托声明看上去和方法声明相似,只是没有实现块。
  2. 使用该委托类型声明一个委托变量。
  3. 创建一个委托类型的对象,委托方法包含指向某个方法的引用,这个方法的签名和返回类型必须跟第一步中定义的委托类型一致。
  4. 在代码中可以像调用方法一样调用委托。

调用委托

  • 可以通过两种方式调用委托。一种像是调用方法一样使用委托;另一种是使用委托的额Invoke方法。
  • 在传递参数时,可以直接将参数放在花括号里。
  • 在调用时委托不能为空(null),否则将引发异常,可以使用if语句来检查,也可以使用空条件运算符和Invoke方法。
  • 如果一个方法在调用列表中多次出现,则在调用委托时,每次在列表中遇到该方法时都会调用它。

委托的示例

没有返回值和参数的委托

namespace CSharpProject1;

// 定义一个没有返回值和参数的委托
delegate void PrintFunction();

class Test
{
    public void Print1()
    {
        Console.WriteLine("Print1 -- instance");
    }

    public static void Print2()
    {
        Console.WriteLine("Print2 -- static");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Test t = new Test();    // 创建一个测试类实例
        PrintFunction pf;       // 创建一个空委托
        pf = t.Print1;          // 实例化并初始化委托
        // 给委托增加3个另外的方法
        pf += Test.Print2;
        pf += t.Print1;
        pf += Test.Print2;
        // 现在委托有4个方法
        
        if (pf != null) pf();//调用委托
        else Console.WriteLine("Delegate is empty");
    }
}

调用带返回值的委托

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

  • 调用列表中最后一个方法返回的值就是委托调用返回的值。
  • 委托列表中所有其他方法的返回值都会忽略。
namespace CSharpProject1;

// 定义一个有返回值的委托
delegate int MyDelegate();

class MyClass
{
    int value = 5;

    public int Add2()
    {
        value += 2;
        return value;
    }
    
    public int Add3()
    {
        value += 3;
        return value;
    }
}

class Program
{
    static void Main(string[] args)
    {
        MyClass mc = new MyClass();
        MyDelegate md = mc.Add2;
        md += mc.Add3;
        Console.WriteLine($"value: {md()}");
    }
}

调用带引用参数的委托

如果委托有引用参数,参数值会根据调用列表中的一个或者多个方法的返回值而改变。在调用委托列表中的下一个方法时,参数的新值(不是初始值)回传给下一个方法。例如:

namespace CSharpProject1;

// 定义一个没有回值带有参数的委托
delegate void MyDeleGate(ref int x);

class Program
{
    public void Add2(ref int x)
    {
        x += 2;
    }

    public void Add3(ref int x)
    {
        x += 3;
    }
    
    static void Main(string[] args)
    {
        Program p = new Program();
        MyDeleGate md = p.Add2;
        md += p.Add3;

        int x = 5;
        md.Invoke(ref x);
        Console.WriteLine($"value:{x}");
    }
}
D:/RiderProjects/CSharpProject1/CSharpProject1/bin/Debug/net8.0/CSharpProject1.exe
value:10

Process finished with exit code 0.

匿名方法

匿名方法是在实例化时内联声明的方法。例如:

使用匿名方法:

namespace CSharpProject1;

class Program
{
    delegate int OtherDelegate(int param);
    static void Main(string[] args)
    {
        OtherDelegate del = delegate(int x)
        {
            return x + 20;
        };
        Console.WriteLine($"{del(5)}");
        Console.WriteLine($"{del(6)}");
    }
}

使用具体方法:

namespace CSharpProject1;

class Program
{
    public static int Add20(int x)
    {
        return x + 20;
    }

    delegate int OtherDelegate(int param);
    
    static void Main(string[] args)
    {
        OtherDelegate del = Add20;
        Console.WriteLine($"{del.Invoke(5)}");
        Console.WriteLine($"{del.Invoke(6)}");
    }
}
D:/RiderProjects/CSharpProject1/CSharpProject1/bin/Debug/net8.0/CSharpProject1.exe
25
26

Process finished with exit code 0.

使用匿名方法

我们可以在如下地方使用匿名方法:

  • 声明委托变量时作为初始化表达式。
  • 组合委托时在赋值语句的右边。
  • 为委托增加事件时在赋值语句的右边。

匿名方法的语法

delegate(int param) {语句块}
posted @ 2024-01-15 13:00  LilyFlower  阅读(15)  评论(0编辑  收藏  举报