zoukankan      html  css  js  c++  java
  • 什么是委托?

    委托(delegate)是一种升级版的“函数指针”。

    • 一切皆地址

        变量(数据)是以某个地址为起点的一段内存中存储的值。比如我们声明了一个变量a,则cpu会找到变量a指向的内存首地址,根据a变量的分配大小,获取一整块属于a的内存。

        函数(算法)是以某个地址为起点的一段内存中存储的机器语言指令。cpu会根据一条条的机器指令来完成我们的算法逻辑。

    • 直接调用和间接调用

        直接调用:通过函数名来调用函数,cpu根据函数名直接获得该函数的所在地址并开始执行。执行完毕后返回到调用者继续向下执行。

        间接调用:通过函数指针来调用函数,cpu根据函数指针存储的值获得函数所在地址并开始执行,执行完毕后返回调用者继续向下执行。

      我们可以看出直接调用和间接调用其实是一致的,都会获取我们想调用的那个函数的所在地址。

    • 强类型委托

        Action委托  Func委托

        这两种简单的委托为我们提供了基本的配置和约束~~   以下为对这两种委托适用的小例子:

    using System;
    
    namespace LanguageLearn
    {
        internal class Program
        {
            static void Main(string[] args)
            {
                var calculator = new Calculator();
                var action = new Action(calculator.Report);
                calculator.Report(); // 直接调用
                action.Invoke();     // 间接调用
                action();            // 函数指针调用形式
    
                Func<int, int, int> funcAdd = new Func<int, int, int>(calculator.Add);
                var addResult = funcAdd.Invoke(10, 20);
                Console.WriteLine(addResult);
    
                Func<int, int, int> funcSub = new Func<int, int, int>(calculator.Sub);
                var subResult = funcSub.Invoke(10, 5);
                Console.WriteLine(subResult);
    
                Func<int, int, int> funcMuti = new Func<int, int, int>((x, y) => x * y);
                var mutiResult = funcMuti.Invoke(10, 5);
                Console.WriteLine(mutiResult);
    
            }
        }
    
        internal class Calculator
        {
            public void Report()
            {
                Console.WriteLine("I am a calculator");
            }
    
            public int Add(int a, int b)
            {
                return a + b;
            }
    
            public int Sub(int a, int b)
            {
                return a - b;
            }
        }
    
    }
    
    //out put
    //I am a calculator
    //I am a calculator
    //I am a calculator
    //30
    //5
    //50
    delegate
    • 委托的声明

        委托是一种数据类型,并且它是一种类,所以它是引用类型,我把它理解成一种特别的引用类型,它是对函数方法类型的一种引用。

        它的声明方式与类的声明不同,关键字为delegate。

        注意声明委托的位置,它不应成为嵌套类型。

        委托与所委托的方法必须“类型兼容”,返回值需一致,入参的参数类型和参数个数也需要一致。

    using System;
    
    namespace LanguageLearn
    {
        internal delegate double CalDelegate(double x, double y);
    
        internal class Program
        {
            static void Main(string[] args)
            {
                var calculator = new Calculator();
                CalDelegate calDelegate1 = new CalDelegate(calculator.Add);
                CalDelegate calDelegate2 = new CalDelegate(calculator.Sub);
                CalDelegate calDelegate3 = new CalDelegate(calculator.Mul);
                CalDelegate calDelegate4 = new CalDelegate(calculator.Div);
                CalDelegate calDelegate5 = new CalDelegate((a, b) => a * b + 1);
               
                double a = 100;
                double b = 200;
                double c = 0;
    
                c = calDelegate1.Invoke(a, b);
                Console.WriteLine(c);
                c = calDelegate2.Invoke(a, b);
                Console.WriteLine(c);
                c = calDelegate3.Invoke(a, b);
                Console.WriteLine(c);
                c = calDelegate4.Invoke(a, b);
                Console.WriteLine(c);
                c = calDelegate5.Invoke(a, b);
                Console.WriteLine(c);
            }
        }
    
        internal class Calculator
        {
            public void Report()
            {
                Console.WriteLine("I am a calculator");
            }
    
            public double Add(double a, double b)
            {
                return a + b;
            }
    
            public double Sub(double a, double b)
            {
                return a - b;
            }
    
            public double Mul(double a, double b)
            {
                return a * b;
            }
    
            public double Div(double a, double b)
            {
                return a / b;
            }
        }
    
    }
    custom delegate
    • 委托的一般使用

        实例:将方法当作参数传递给另一个方法

        解释:我们在方法体的内部,适用参数传递进来的委托,间接的调用封装在委托内的方法。形成了一种动态调用方法的结构。

          正确使用1:模板方法,“借用” 指定的外部方法来产生结果 (在方法体中有一部分的空缺需要根据你传递的方法来完成)

          1. 相当于“填空题”
          2. 常位于代码的中部
          3. 常具有返回值 

          正确使用2:回调(callback)方法,调用指定的外部方法 (类似于“提供商”,当你需要哪种服务的时候,可以根据不同的“提供商”来调用你需要的委托方法)

          1. 相当于“流水线” (我们在主调方法中,根据主调方法的条件来决定是否调用委托来回调委托封装的方法)
          2. 常位于代码的尾部
          3. 委托无返回值                

        模板方法实例:利用方法委托参数,我们可以在不改变某些核心逻辑代码的情况下,利用委托作为模板,我们可以调用不同的方法,以达到不同的效果,在我理解,这是除了多态或工厂模式的另外一种减小了耦合度,遵循了开闭原则,的一种代码高复用的实现形式。

    using System;
    using System.Text.Json;
    
    namespace LanguageLearn
    {
        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.MakeToyCar);
    
                Box box1 = wrapFactory.WrapProduct(func1);
                Box box2 = wrapFactory.WrapProduct(func2);
    
                Console.WriteLine(JsonSerializer.Serialize<Box>(box1));
                Console.WriteLine(JsonSerializer.Serialize<Box>(box2));
            }
        }
    
        class Product
        {
            public string Name { get; set; }
        }
    
        class Box
        {
            public Product Product { get; set; }
        }
    
        class WrapFactory
        {
            public Box WrapProduct(Func<Product> GetProduct)
            {
                var box = new Box();
                box.Product = GetProduct.Invoke();
                return box;
            }
        }
    
        class ProductFactory
        {
            public Product MakePizza()
            {
                Product product = new Product();
                product.Name = "Pizza";
                return product;
            }
    
            public Product MakeToyCar()
            {
                Product product = new Product();
                product.Name = "ToyCar";
                return product;
            }
        }
    
    }
    
    // out put
    //{ "Product":{ "Name":"Pizza"} }
    //{ "Product":{ "Name":"ToyCar"} }
    模板委托使用

        回调方法实例:根据方法中的执行条件,我们可以按需的执行回调方法,回调方法通常是一些Action委托,可用与做一些记录或特殊方法的再次调用。

    using System;
    using System.Text.Json;
    
    namespace LanguageLearn
    {
        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.MakeToyCar);
    
                Logger logger = new Logger();
                Action<Product> log = new Action<Product>(logger.Log);
    
                Box box1 = wrapFactory.WrapProduct(func1, log);
                Box box2 = wrapFactory.WrapProduct(func2, log);
    
                Console.WriteLine(JsonSerializer.Serialize<Box>(box1));
                Console.WriteLine(JsonSerializer.Serialize<Box>(box2));
            }
        }
    
    
        class Logger
        {
            public void Log(Product product)
            {
                Console.WriteLine($"Product {product.Name} created at {DateTime.UtcNow}, price is {product.Price}");
            }
        }
    
        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)
            {
                var box = new Box();
                var 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 = 30;
                return product;
            }
    
            public Product MakeToyCar()
            {
                Product product = new Product();
                product.Name = "ToyCar";
                product.Price = 80;
                return product;
            }
        }
    
    }
    
    // out put
    // Product ToyCar created at 10/10/2021 6:54:13 AM, price is 80
    //{ "Product":{ "Name":"Pizza","Price":30} }
    //{ "Product":{ "Name":"ToyCar","Price":80} }
    回调委托使用
    • 委托的高阶使用

          委托的单播和多播调用

          委托的隐式异步调用以及显式异步调用

    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace LanguageLearn
    {
        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 action = new Action(stu1.DoHomeWork);
                //action += stu2.DoHomeWork;
                //action += stu3.DoHomeWork;
               
                Task task1 = new Task(new Action(stu1.DoHomeWork));
                Task task2 = new Task(new Action(stu2.DoHomeWork));
                Task task3 = new Task(new Action(stu3.DoHomeWork));
    
                task1.Start();
                task2.Start();
                task3.Start();
    
                // 显式线程调用
                //Action action1 = new Action(stu1.DoHomeWork);
                //Action action2 = new Action(stu2.DoHomeWork);
                //Action action3 = new Action(stu3.DoHomeWork);
    
                //Thread thread1 = new Thread(new ThreadStart(action1));
                //Thread thread2 = new Thread(new ThreadStart(action2));
                //Thread thread3 = new Thread(new ThreadStart(action3));
    
                //thread1.Start();
                //thread2.Start();
                //thread3.Start();
    
                // 隐式异步调用 在.NET FrameWork中被支持
                //action1.BeginInvoke(null, null);
                //action2.BeginInvoke(null, null);
                //action3.BeginInvoke(null, null);
    
                for (int i = 0; i < 10; i++)
                {
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.WriteLine($"Main thread count {i}");
                    Thread.Sleep(1000);
                }
            }
        }
    
        class Student
        {
            public int Id { get; set; }
            public ConsoleColor PenColor { get; set; }
    
            public void DoHomeWork()
            {
                for (int i = 1; i <= 3; i++)
                {
                    Console.ForegroundColor = PenColor;
                    Console.WriteLine($"Student {Id} doing homeworks {i} hour");
                    Thread.Sleep(1000);
                }
            }
        }
    }
    委托的多播和异步
  • 相关阅读:
    python接口自动化(三)--如何设计接口测试用例(详解)
    python接口自动化(二)--什么是接口测试、为什么要做接口测试(详解)
    python接口自动化(一)--什么是接口、接口优势、类型(详解)
    PostgreSQL建立分区表示例
    PostgreSQL中的时间操作总结
    linux系统命令:yum和apt-get
    oracle中的连接查询与合并查询总结
    oracle中时间运算
    oracle中一些用法总结
    oracle中substr与instr
  • 原文地址:https://www.cnblogs.com/Xieyiincuit/p/15389624.html
Copyright © 2011-2022 走看看