2021-06-18-刘铁猛C#语言入门详解-学习笔记P19委托

P19委托

与2021-12-22-《C#基础+WinForms》-内容总结-P11 委托+Lambda配合食用
回溯:2022/01/11/12/13
一、P19内容总结

  1. 介绍委托
  2. 委托的声明
  3. 委托的使用

二、什么是委托delegate

  1. 委托是函数指针的升级版
    1)举例说明:C/C++中的函数指针
  2. 程序员常提及的概念:一切皆地址
    1)变量(数据)是以某个地址为起点的一段内存中所存储的值
    解释:数据存储在变量中,变量的本质:以变量名所对应的内存地址为起点的一段内存[存储着变量的数据],内存的大小由数据类型决定
    2)函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令
    函数的本质:以函数名所对应的内存地址为起点的一段内存,在这段内存中存储的是一组机器语言的指令,CPU按照这组指令一条一条完成
  3. 直接调用&间接调用
    1)直接调用:通过函数名调用函数,CPU通过函数名直接获得函数的地址并开始执行→返回
    2)间接调用:通过函数指针调用函数,CPU通过读取函数指针中存储的值获得函数的地址并开始执行→返回
  4. 委托的简单使用
	//C#中已经声明好的委托类型举例
	//Action委托与Func委托
    class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            //Action委托指向的是参数列表为空,返回值为void的方法
            Action action = new Action(calculator.Report);
            //直接调用
            calculator.Report();
            //间接调用1
            action.Invoke();
            //间接调用2
            action();

            /*Func<>委托指向的是有参数、有返回值的方法
            Func<参数,参数,返回值>*/
            Func<int, int, int> func1 = new Func<int,int,int>(calculator.Add);
            Func<int, int, int> func2 = new Func<int, int, int>(calculator.Sub);

            int x = 100;
            int y = 200;
            int z = 0;

            //间接调用1
            z = func1.Invoke(x, y);
            Console.WriteLine(z);
            z = func2.Invoke(x, y);
            Console.WriteLine(z);
            //间接调用2
            z = func1(x, y);
            Console.WriteLine(z);
            z = func2(x, y);
            Console.WriteLine(z);
        }
    }
    //有一个Calculator类有三种方法
    class Calculator
    {
        public void Report()
        {
            Console.WriteLine("I have 3 methods.");
        }

        public int Add(int a,int b)
        {
            int result = a + b;
            return result;
        }

        public int Sub(int a, int b)
        {
            int result = a - b;
            return result;
        }
    }

三、委托的声明[自定义委托]

  1. 关于委托
    1)委托是一种类class,类是数据类型所以委托也是一种数据类型
    2)委托声明在名称空间体中
  2. 委托声明方式与类不同
    1)自定义委托举例
	//声明返回值为double类型,参数列表为double类型的委托
    public delegate double Calc(double x, double y);
    class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Calc calc1 = new Calc(calculator.Add);
            Calc calc2 = new Calc(calculator.Sub);
            Calc calc3 = new Calc(calculator.Mul);
            Calc calc4 = new Calc(calculator.Div);

            double x = 100;
            double y = 200;
            double z = 0;

            //间接调用1
            z = calc1.Invoke(x, y);
            Console.WriteLine(z);
            z = calc2.Invoke(x, y);
            Console.WriteLine(z);
            z = calc3.Invoke(x, y);
            Console.WriteLine(z);
            z = calc4.Invoke(x, y);
            Console.WriteLine(z);

            //间接调用2
            z = calc1(x, y);
            Console.WriteLine(z);
            z = calc2(x, y);
            Console.WriteLine(z);
            z = calc3(x, y);
            Console.WriteLine(z);
            z = calc4(x, y);
            Console.WriteLine(z);
        }
    }
    class Calculator
    {
        public double Add(double a,double b)
        {
            double result = a + b;
            return result;
        }

        public double Sub(double a, double b)
        {
            double result = a - b;
            return result;
        }

        public double Mul(double a, double b)
        {
            double result = a * b;
            return result;
        }

        public double Div(double a, double b)
        {
            double result = a / b;
            return result;
        }
    }
  1. 委托与所封装的方法必须“类型兼容”
    1)返回值数据类型一致
    2)参数列表个数、数据类型要一致

四、委托的使用

委托的一般使用

  1. 实例:把方法当作参数传给另一个方法
    1)模板方法,借用指定的外部方法产生结果
	class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory();
            WrapFactory wrapFactory = new WrapFactory();
            //准备委托类型的参数,传入方法模板
            Func<Product> fun1 = new Func<Product>(productFactory.MakePizza);
            Func<Product> fun2 = new Func<Product>(productFactory.MakeToyCar);

            //包装产品
            Box box1 = wrapFactory.WrapProduct(fun1);
            Box box2 = wrapFactory.WrapProduct(fun2);

            Console.WriteLine(box1.Product.Name);
            Console.WriteLine(box2.Product.Name);
        }
    }

    class Product
    {
        public string Name { get; set; }
    }

    class Box
    {
    	//每个包装箱具有一个Product类型的属性--箱子里包装的产品
        public Product Product { get; set; }
    }

    //把产品包上盒子交给用户
    class WrapFactory
    {
        //模板方法:传入一个名字,把名字放在box里打包,返回打包后的box
        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 MakeToyCar()
        {
            Product product = new Product();
            product.Name = "ToyCar";
            return product;
        }
    }

2)回调方法callback,调用指定的外部方法

	class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory();
            WrapFactory wrapFactory = new WrapFactory();
            //准备委托类型的参数,传入方法模板
            Func<Product> fun1 = new Func<Product>(productFactory.MakePizza);
            Func<Product> fun2 = new Func<Product>(productFactory.MakeToyCar);

            Logger logger = new Logger();
            Action<Product> log = new Action<Product>(logger.Log);

            //包装产品
            Box box1 = wrapFactory.WrapProduct(fun1,log);
            Box box2 = wrapFactory.WrapProduct(fun2,log);

            Console.WriteLine(box1.Product.Name);
            Console.WriteLine(box2.Product.Name);
        }
    }

    //记录程序的运行状态,没有返回值
    class Logger
    {
        public void Log(Product product)
        {
            Console.WriteLine("Produce '{0}' created at {1}.Price is {2}", product.Name, DateTime.UtcNow, 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)
        {
            Box box = new Box();
            Product product = getProduct.Invoke();
            if(product.Price>=50)
            {
                logCallback(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 MakeToyCar()
        {
            Product product = new Product();
            product.Name = "ToyCar";
            product.Price = 100;
            return product;
        }
    }
  1. 滥用委托的后果
    1)可读性下降,debug难度增加
    2)委托回调、异步调用、多线程纠缠在一起,代码变得难以阅读与维护
    PS:老师举了个他之前的项目,恕我直言,我需要时间消化,等后面我看懂了再说
    3)委托使用不当会造成内存泄漏、程序性能下降

委托的高级使用

  1. 单播委托、多播委托
	//单播委托:一个委托内封装一个方法
	class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1,PenColor=ConsoleColor.Red };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };

            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 < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s).", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }
	//多播委托:一个委托内封装的不只是一个方法
	class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1,PenColor = ConsoleColor.Red };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };
            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 < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s).", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }
  1. 隐式异步调用
    1)同步与异步简介
    同步:你先做完,我在你基础上接着做
    异步:你我同时做
    2)同步调用与异步调用对比
    每个运行的程序是一个进程
    每个进程中可以有一个或多个线程
    同步调用是在同一个线程内
    异步调用底层机理是多线程
    同步 串行 单线程,异步 并行 多线程

同步调用 举例:直接同步调用、间接同步调用 (单播、多播)
注意:“间接–直接”和“同步–异步”两个概念

//直接同步调用
//用方法的名字调用该方法
	class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1,PenColor=ConsoleColor.Red };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };

            //直接调用三个方法,然后主线程还有一些事情要做
            stu1.DoHomework();
            stu2.DoHomework();
            stu3.DoHomework();
            for (int i = 0; i < 10; i++)
            {
                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 < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s).", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }
	//间接同步调用(单播委托)
	class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1,PenColor=ConsoleColor.Red };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };

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

            action1.Invoke();
            action2.Invoke();
            action3.Invoke();

            for (int i = 0; i < 10; i++)
            {
                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 < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s).", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }
	//间接同步调用(多播委托)
	class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1,PenColor=ConsoleColor.Red };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };

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

            action1+=action2;
            action1+=action3;
            action1.Invoke();

            for (int i = 0; i < 10; i++)
            {
                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 < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s).", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }

异步调用 隐式多线程&显示多线程
隐式异步调用 BeginInvoke方法

记住:隐式多线程的BeginInvoke方法

若编译程序发生异常
提示:Operation is not supported on this platform.
原因:创建的是.NET Core项目,但是在.NET Core中不支持Action.BeginInvoke(null, null)的委托异步调用方法。
解决办法:重新创建一个.NET Framework项目,把代码重新复制过去后,就没有异常了。

	//隐式多线程
	class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1, PenColor=ConsoleColor.Red };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };

            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 < 10; i++)
            {
                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 < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s).", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }

	//显示多线程 自己声明多线程
	//方法一 使用Thread
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1,PenColor=ConsoleColor.Red };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };

            Thread thread1 = new Thread(new ThreadStart(stu1.DoHomework));
            Thread thread2 = new Thread(new ThreadStart(stu2.DoHomework));
            Thread thread3 = new Thread(new ThreadStart(stu3.DoHomework));

            thread1.Start();
            thread2.Start();
            thread3.Start();
            for (int i = 0; i < 10; i++)
            {
                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 < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s).", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }
	
	//方法二Task
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student() { ID = 1,PenColor=ConsoleColor.Red };
            Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
            Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };

            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();

            for (int i = 0; i < 10; i++)
            {
                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 < 5; i++)
            {
                Console.ForegroundColor = this.PenColor;
                Console.WriteLine("Student {0} doing homework {1} hour(s).", this.ID, i);
                Thread.Sleep(1000);
            }
        }
    }

多线程的运行结果中其他三个线程颜色会不相同
但是由于多个线程访问同一个资源时,在争抢资源时发生冲突,所以跟理想状态不相同
为解决这个问题,可以“加锁”–高级内容,未来再说吧

  1. 应该适当使用接口取代一些对委托的功能
    用产品例子说明了接口
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            PizzaFactory pizzaFactory = new PizzaFactory();
            CarToyFactory carToyFactory = new CarToyFactory();
            WrapFactory wrapFactory = new WrapFactory();

            //包装产品
            Box box1 = wrapFactory.WrapProduct(pizzaFactory);
            Box box2 = wrapFactory.WrapProduct(carToyFactory);

            Console.WriteLine(box1.Product.Name);
            Console.WriteLine(box2.Product.Name);
        }
    }
    class Product
    {
        public string Name { get; set; }
    }
    interface IProductFactory
    {
        Product Made();
    }
    class PizzaFactory : IProductFactory
    {
        public Product Made()
        {
            Product product = new Product();
            product.Name = "Pizza";
            return product;
        }
    }
    class CarToyFactory : IProductFactory
    {
        public Product Made()
        {
            Product product = new Product();
            product.Name = "ToyCar";
            return product;
        }
    }
    class Box
    {
        //每个包装箱具有一个Product类型的属性--箱子里包装的产品
        public Product Product { get; set; }
    }
    //把产品包上盒子交给用户
    class WrapFactory
    {
        //模板方法:传入一个名字,把名字放在box里打包,返回打包后的box
        public Box WrapProduct(IProductFactory productFactory)
        {
            Box box = new Box();
            Product product = productFactory.Made();
            box.Product = product;
            return box;
        }
    }
}

你可能感兴趣的:(刘铁猛C#语言入门详解,c#)