C#委托

C#委托

C# 中的委托(Delegate)类似于 C 或 C++ 中的函数指针是一种引用类型表示对具有特定参数列表和返回类型的方法的引用委托特别适用于实现事件和回调方法,所有的委托都派生自 System.Delegate 类。在实例化委托时,可以将委托的实例与具有相同返回值类型的方法相关联,这样就可以通过委托来调用方法。另外,使用委托还可以将方法作为参数传递给其他方法,

委托具有以下特点:

  • 委托类似于 C/C++ 中的函数指针,但委托是完全面向对象的。另外,C++ 中的指针会记住函数,而委托则是同时封装对象实例和方法;
  • 委托允许将方法作为参数进行传递;
  • 委托可用于定义回调方法;
  • 委托可以链接在一起,例如可以对一个事件调用多个方法;
  • 方法不必与委托类型完全匹配;
  • C# 2.0 版引入了匿名函数的概念,可以将代码块作为参数(而不是单独定义的方法)进行传递。C# 3.0 引入了 Lambda 表达式,利用它们可以更简练地编写内联代码块。匿名方法和 Lambda 表达式都可编译为委托类型,这些功能现在统称为匿名函数。

委托也是一种类型,特性类型,初始化需要一个方法支持,委托是记录方法信息的一种类型

有些逻辑你不想写死的,就可以将这些方法独立出来,然后通过委托来传递。

1、声明委托

声明委托需要使用 delegate 关键字,语法格式如下:

//定义方式:delegate + 返回值 + 委托名称 + (参数类型 参数名称)
delegate <return type> delegate-name(<parameter list>)

其中 return type 为返回值类型,delegate-name 为委托的名称,parameter list 为参数列表。

提示:委托可以引用与委托具有相同签名的方法,也就是说委托在声明时即确定了委托可以引用的方法。

2、自定义委托

定义和Class类型相似

类的定义和使用:

MyClass myclass=new MyClass(); //初始化
my.Hello();    //类里面方法的调用

委托的定义和使用:

//【1】声明委托
delegate void HelloDelegate(string msg); 


//【2】定义委托方法
//定义委托方法的时候,返回值类型和参数要一样包括(参数类型,顺序,参数个数)也就是委托签名
public static void Hello(string msg){
    Console.WriteLine("你好,我是委托");
}


//【3】委托初始化
HelloDelegate hello=new HelloDelegate(Hello);


//【4】委托调用
hello.Invoke(""); //从这里可以看出来,调用委托就是调用了初始化委托的方法

//委托调用也可以简写为以下的形式
hello("");   //可以不用Invoke()方法,直接以括号代替传递参数。

这里需要注意一下委托的一些使用注意项:

  • 委托可以定义在类里面,类外面,也可以在命名空间下,根据不同的需要来确定委托方法的使用范围。

  • 委托一旦声明,想要使用就必须使用 new 关键字来创建委托的对象,同时将其与特定的方法关联。

3、泛型委托

在学习泛型委托之前,我们回顾一下泛型的特点:代码重用,可以写出通用的代码

之前我们了解过什么是泛型集合,现在用下面代码演示一下泛型委托:

//【1】这里用泛型的方式定义委托
delegate  void  AddDelegate<T>(T a,T b);  


//【2】定义方法
public static void AddInt(int a, int b)
{
}
public static void AddDouble(double a,double b)
{
}
public static void AddBool(bool a,bool b)
{
}

//【3】委托初始化
AddDelegate<int> addDelegateInt = new AddDelegate<int>(AddInt);
addDelegateInt(1,3);
//.....

通过以上的代码可以发现,AddDelegate(T a,T b) 中,T可以为任意的类型。如此一来,该泛型委托一个,可以匹配多个方法。(通用的委托)

4、预定义委托

系统定义的一些委托,这里介绍两个比较常用的委托:ActionFunc

4.1 Action
image-20221015002452014

无返回值的委托,一共有16个可传参数。(一般是用不到16个参数,16个够用了)

下面看代码示例:

//委托初始化和调用
Action<string, int> action = new Action<string, int>(Show);
action("无返回值委托", 1);
Action<int, int, int> action1 = new Action<int, int, int>(Show1);
action1(1, 2, 3);

//这里需要根据委托对应的签名来委托定义方法
public static void Show(string a,int b)
{
  Console.WriteLine(a);
}
public static void Show1(int a, int b,int c)
{
  Console.WriteLine(a+b+c);
}

因为ActionFunc是系统内部已经定义好了,我们只需要拿出来用就行了。当然,方法还是需要自己根据对应委托签名进行创建。

4.2 Func

有返回值的委托,Func有返回值委托一共有17个,其中最后一个为返回值类型,其他位参数

此委托的定义如下:

public delegate TResult Func<in T, out TResult>(T arg)

(1)in T :此委托封装的方法的参数类型。

(2)out TResult :此委托封装的方法的返回值类型。

        //【1】初始化委托
	Func<string> func = new Func<string>(Show2);
        Console.WriteLine(func()); //【3】调用

        Func<int, int> func1 = new Func<int, int>(Show3);
        Console.WriteLine(func1(2));//【3】调用
		//【2】定义方法
        public static int Show3(int a)
        {
            return a;
        }
        public static string Show2()
        {
            return "有返回值";
        }   
        public static int Show3(int a)
        {
            return a;
        }
        public static string Show2()
        {
            return "有返回值";
        }

5、Lambda表达式+委托

lambda表达式本质上是一个匿名函数, 用 =>来表示,念作goes to

现在用代码演示一下lambda表达式结合委托来使用:

 	    Action<string> action = new Action<string>(Show);
            action("您好我是泛型委托Action");

            Action<string> action1 = new Action<string>(delegate (string msg) {
                Console.WriteLine(msg);
            });
            action1("您好我是泛型委托Action,使用了匿名方法");

            Action<string> action2 = new Action<string>((string msg)=>{
                Console.WriteLine(msg);
            });
            action2("您好我是泛型委托Action,使用了Lambda表达式");

            //一个参数 一个语句的方法
            Action<string> action3 = new Action<string>(msg => Console.WriteLine(msg));
            action3("一个参数 一个语句的方法");

            //无返回值,无参数
            Action action4 = new Action(()=> Console.WriteLine("无返回值,无参数"));
            action4();


            //有返回值 ,单条件语句
            Func<string> func = new Func<string>(() => {
                Console.WriteLine("这里是代码块编写方式");
                return "有返回值,但条件语句";
            });
            string msg=func();
            Func<string> func1 = new Func<string>(() => "有返回值,但条件语句");
            func1();
 	    public static void Show(string msg)
            {
                Console.WriteLine(msg);
            }

演示中代码,从上到下看,可以发现原来是给委托实例添加一个方法,然后将方法简化成匿名函数放了进去,慢慢一步步简化代码。这里我们需要记住,lambda表达式本质上是一个匿名函数(方法)

注意:在写lambda表达式的时候,需要根据是否为单语句,函数有返回值等情况进行编写,格式不正确就会报错。例如,无返回值直接 ()=>""运行这样的格式写,但是如果有返回值的话则需要加上代码块{ }然后return。

3、多播委托(合并委托)

委托对象有一个非常有用的属性,那就是可以通过使用+运算符将多个对象分配给一个委托实例,同时还可以使用-运算符从委托中移除已分配的对象,当委托被调用时会依次调用列表中的委托。委托的这个属性被称为委托的多播,也可称为组播,利用委托的这个属性,您可以创建一个调用委托时要调用的方法列表。

注意:仅可合并类型相同的委托。

下面通过示例程序演示委托的多播:

using System;
delegate int NumberChanger(int n);      // 定义委托
namespace c.biancheng.net
{
    class Demo
    {
        static int num = 10;
        public static int AddNum(int p){
            num += p;
            return num;
        }
        public static int MultNum(int q){
            num *= q;
            return num;
        }
        public static int getNum(){
            return num;
        }
        static void Main(string[] args){
            // 创建委托实例
            NumberChanger nc;
            NumberChanger nc1 = new NumberChanger(AddNum);
            NumberChanger nc2 = new NumberChanger(MultNum);
            nc = nc1;
            nc += nc2;
            // 调用多播
            nc(5);
            Console.WriteLine("num 的值为: {0}", getNum());
            Console.ReadKey();
        }
    }
}

运行结果如下:

num 的值为: 75

【示例】下面定义一个委托 printString,我们使用这个委托来调用两个方法,第一个把字符串打印到控制台,第二个把字符串打印到文件:

using System;
using System.IO;
namespace c.biancheng.net
{
    class Demo
    {
        static FileStream fs;
        static StreamWriter sw;
        // 委托声明
        public delegate void printString(string s);
        // 该方法打印到控制台
        public static void WriteToScreen(string str){
            Console.WriteLine("The String is: {0}", str);
        }
        // 该方法打印到文件
        public static void WriteToFile(string s){
            fs = new FileStream("./message.txt", FileMode.Append, FileAccess.Write);
            sw = new StreamWriter(fs);
            sw.WriteLine(s);
            sw.Flush();
            sw.Close();
            fs.Close();
        }
        // 该方法把委托作为参数,并使用它调用方法
        public static void sendString(printString ps)
        {
            ps("C#轮播委托");
        }
        static void Main(string[] args){
            printString ps1 = new printString(WriteToScreen);
            printString ps2 = new printString(WriteToFile);
            sendString(ps1);
            sendString(ps2);
            Console.ReadKey();
        }
    }
}

运行上面的代码会在程序所在的目录下生成一个名为 message.txt 的文件,并输出一下内容:

The String is: C#轮播委托
posted @ 2022-10-16 14:21  travellerα  阅读(62)  评论(0)    收藏  举报