C# 委托

本文介绍了C#中的委托,它是函数指针的升级版,允许将方法作为参数传递。文中详细讲解了Action和Func委托的使用,以及如何自定义委托。通过示例展示了委托在模板方法和回调方法中的应用,同时提到了委托的潜在缺点,如紧耦合和可能影响代码可读性。此外,还探讨了单播和多播委托,以及异步调用的不同方式。
摘要由CSDN通过智能技术生成

前言

委托: 函数指针的“升级版”;(C/C++中的函数指针)

委托分类: Action委托、Function委托、自定义委托;

”一切皆地址“: 程序(本质) = 数据 + 算法;(变量(数据) 是以某个地址为起点的一段内存中所存储的值;函数(算法) 是以某个地址为起点的一段内存中所存储的一组机器语言指令)

Java 中没有与委托相对应的功能实体

什么是委托?

一句话解释就是:将方法当作参数传入另一个方法的参数。 .net中有很多常见的委托如:Func 、Action 作用:提高方法的扩展性

  • 委托从字面上理解就是一种代理,类似于房屋中介,由租房人委托中介为其租赁房屋。
  • 在 C#语言中,委托则委托某个方法来实现具体的功能。
  • 委托是一种引用类型,虽然在定义委托时与方法有些相似,但不能将其称为方法。
  • 委托在使用时遵循三步走的原则,即定义声明委托、实例化委托以及调用委托。
  • 从数据结构来讲,委托是和类一样是一种用户自定义类型。
  • 委托是方法的抽象,它存储的就是一系列具有相同签名和返回回类型的方法的地址。
  • 调用委托的时候,委托包含的所有方法将被执行。
  • 委托是 C# 语言中的一个特色,通常将委托分为命名方法委托、多播委托、匿名委托,其中命名方法委托是使用最多的一种委托。

三种委托的使用

Action 委托: 指向无返回值成员;

using System;

namespace InterfaceTest
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Action action = new Action(calculator.Report);//Action委托

            calculator.Report();//直接调用
            action.Invoke();//间接调用
            action();//间接调用
        }
    }
    class Calculator
    {
        public void Report()
        {
            System.Console.WriteLine("I have 3 methods");
        }

        public int Sum(int a, int b)
        {
            return a + b;
        }
    }

Function委托: 指向有返回值成员(Func<参数1类型,参数2类型,返回值类型>)

using System;

namespace InterfaceTest
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Func<int, int, int> func = new Func<int, int, int>(calculator.Sum);//func<>委托

            calculator.Sum(2, 3);//直接调用
            func.Invoke(2, 3);//间接调用
            func(2, 3); //间接调用
        }
    }
    class Calculator
    {
        public void Report()
        {
            System.Console.WriteLine("I have 3 methods");
        }

        public int Sum(int a, int b)
        {
            return a + b;
        }
    }
}

自定义委托: 【修饰符】 delegate 【返回类型】【名称】(【参数列表】) (例如:public delegate int Add(int x,int y))

using System;

namespace InterfaceTest
{
    public delegate double Cal(double x, double y);//委托声明
    class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Cal cal = new Cal(calculator.Sum);//委托实例化,方式一
            Cal cal1 = calculator.Sub;//委托实例化,方式二

            System.Console.WriteLine(cal(3, 2));
            System.Console.WriteLine(cal1(3, 2));
        }
    }
    class Calculator
    {
        public void Report()
        {
            System.Console.WriteLine("I have 3 methods");
        }
        public double Sum(double a, double b)
        {
            return a + b;
        }
        public double Sub(double a, double b)
        {
            return a - b;
        }
    }
}

委托的使用

模板方法:借用指定的外部方法产生结果(可最大限度重用代码)

  1. 相当于“填空题”;
  2. 常位于代码中部;
  3. 委托有返回值
using System;

namespace InterfaceTest
{
    public delegate double Cal(double x, double y);//委托声明
    class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory(); //声明产品方法
            WrapFactory wrapFactory = new WrapFactory();  //声明产品封装方法

            Func<Product> func1 = new Func<Product>(productFactory.MakePizza);//声明委托方法,获取产品名字信息,并作为产品封装方法的传入参数
            Func<Product> func2 = new Func<Product>(productFactory.MakeToCar);

            Box box1 = wrapFactory.WrapProduct(func1); //调用产品封装方法,输出含产品名字信息的box类
            Box box2 = wrapFactory.WrapProduct(func2);

            System.Console.WriteLine(box1.Product.Name); //输出结果
            System.Console.WriteLine(box2.Product.Name);
        }
    }
    class Product
    {
        public string Name { get; set; }
    }
    class Box
    {
        public Product Product { get; set; }
    }
    class WrapFactory
    {
        //传入委托参数,内部使用委托方法
        public Box WrapProduct(Func<Product> getProduct)
        {
            Box box = new Box();
            Product product = getProduct.Invoke();//调用委托方法,即所传入委托参数的成员类型
            box.Product = product;
            return box;
        }
    }
    class ProductFactory
    {
        public Product MakePizza()
        {
            Product product = new Product();
            product.Name = "Pizza";
            return product;
        }

        public Product MakeToCar()
        {
            Product product = new Product();
            product.Name = "MakeToCar";
            return product;
        }
    }
}

回调方法:调用指定的外部方法

  1. 相当于“流水线”;
  2. 常位于代码末尾;
  3. 委托无返回值
using System;

namespace InterfaceTest
{
    public delegate double Cal(double x, double y);//委托声明
    class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory(); //声明产品方法
            WrapFactory wrapFactory = new WrapFactory();  //声明产品封装方法

            Func<Product> func1 = new Func<Product>(productFactory.MakePizza);//声明委托方法,获取产品名字信息,并作为产品封装方法的传入参数
            Func<Product> func2 = new Func<Product>(productFactory.MakeToCar);

            Logger logger = new Logger(); //声明产品记录方法
            Action<Product> log = new Action<Product>(logger.Log);

            Box box1 = wrapFactory.WrapProduct(func1, log); //调用产品封装方法,输出含产品名字信息的box类
            Box box2 = wrapFactory.WrapProduct(func2, log);

            System.Console.WriteLine(box1.Product.Name); //输出结果
            System.Console.WriteLine(box2.Product.Name);
        }
    }

    //定义记录产品信息类
    class Logger
    {
        public void Log(Product product)
        {
            System.Console.WriteLine("Product'{0}' create at {1} Price is {2}.", product.Name, DateTime.UtcNow, product.Price); //utcNow为不带时区的时间形式
        }
    }
    class Product
    {
        public string Name { get; set; }
        public double Price { get; set; }
    }
    class Box
    {
        public Product Product { get; set; }
    }
    class WrapFactory
    {
        //传入委托参数,内部使用委托方法
        public Box WrapProduct(Func<Product> getProduct, Action<Product> logCallBack)
        {
            Box box = new Box();
            Product product = getProduct.Invoke();//调用委托方法,即所传入委托参数的成员类型
            if (product.Price > 50)
            {
                logCallBack.Invoke(product);
            }
            box.Product = product;
            return box;
        }
    }
    class ProductFactory
    {
        public Product MakePizza()
        {
            Product product = new Product();
            product.Name = "Pizza";
            product.Price = 12;
            return product;
        }

        public Product MakeToCar()
        {
            Product product = new Product();
            product.Name = "MakeToCar";
            product.Price = 100;
            return product;
        }
    }
}

委托的缺点

  1. 是方法级别的紧耦合,工作使用要慎重;
  2. 会使代码可读性下降,debug的难度增加;
  3. 把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护;
  4. 委托使用不当会造成内存泄漏和程序性能下降

委托的高级使用

单播委托:一个委托封装一个方法;

using System;
using System.Threading;

namespace InterfaceTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };

            Action action1 = new Action(stu1.DoHomework);
            Action action2 = new Action(stu2.DoHomework);
            Action action3 = new Action(stu3.DoHomework);
            action1.Invoke();	//单播委托
            action2.Invoke();
            action3.Invoke();
        }
    }

    class Student
    {
        public int ID { get; set; }
        public ConsoleColor PenColor { get; set; }
        public void DoHomework()
        {
            for (int i = 0; i < 3; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hours", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }
}

结果

在这里插入图片描述

 多播(Multicast)委托:一个委托封装多个方法,调用执行顺序为封装方法的先后顺序

using System;
using System.Threading;

namespace InterfaceTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };

            Action action1 = new Action(stu1.DoHomework);
            Action action2 = new Action(stu2.DoHomework);
            Action action3 = new Action(stu3.DoHomework);

            action1 += action2;	//多播委托
            action1 += action3;

            action1.Invoke();
        }
    }

    class Student
    {
        public int ID { get; set; }
        public ConsoleColor PenColor { get; set; }
        public void DoHomework()
        {
            for (int i = 0; i < 3; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hours", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }
}

结果:同上

在这里插入图片描述

异步调用

同步调用和异步调用

  • 直接同步调用:使用方法名;
  • 间接同步调用:使用单播/多播委托的Invoke方法;
  • 隐式异步调用:使用委托的BeginInvoke;
  • 显示异步调用:使用Thread 或 Task

隐式异步调用(.Net Core 框架不适用,.Net Framework框架可以)

using System;
using System.Threading;

namespace InterfaceTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };

            Action action1 = new Action(stu1.DoHomework);  //委托间接调用
            Action action2 = new Action(stu2.DoHomework);
            Action action3 = new Action(stu3.DoHomework);

            action1.BeginInvoke(null, null); //委托隐式异步调用
            action2.BeginInvoke(null, null);
            action3.BeginInvoke(null, null);

            for (int i = 0; i < 5; i++)
            {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Main thread {0}", i);
                Thread.Sleep(1000);
            }
        }
    }

    class Student
    {
        public int ID { get; set; }
        public ConsoleColor PenColor { get; set; }
        public void DoHomework()
        {
            for (int i = 0; i < 3; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hours", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }
}

结果

在这里插入图片描述

 显示异步调用(Thread / Task)

using System;
using System.Threading;

namespace InterfaceTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red };

            Thread thread1 = new Thread(stu1.DoHomework);  //显示异步调用Thread
            Thread thread2 = new Thread(stu2.DoHomework);
            Thread thread3 = new Thread(stu3.DoHomework);

            thread1.Start();
            thread2.Start();
            thread3.Start();

            for (int i = 0; i < 5; i++)
            {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Main thread {0}", i);
                Thread.Sleep(1000);
            }
        }
    }

    class Student
    {
        public int ID { get; set; }
        public ConsoleColor PenColor { get; set; }
        public void DoHomework()
        {
            for (int i = 0; i < 3; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hours", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }
}

结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值