(六)CSharp-刘铁锰版-委托

一、委托概念

1、委托(delegate)是函数指针的“升级版”

  • 实例:C/C++ 中的函数指针(委托相当于函数指针)

一切皆地址

  • 变量(数据)是以某个地址为起点的一段内存中所存储的值
  • 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令

直接调用与间接调用

  • 直接调用:通过函数名来调用函数,CPU 通过函数名直接获得函数函数所在地址并开始执行->返回
  • 间接调用:通过函数指针来调用函数,CPU 通过读取函数指针存储的值获得函数所在地址并开始执行->返回

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

2、委托的简单使用

  • Action 委托(无带参)
  • Func 委托(带参)
    class Program
    {
        static void Main(string[] args)
        {

            Calculator calculator = new Calculator();
            Action action = new Action(calculator.Report);
            calculator.Report();//直接调用
            action.Invoke();
            action();//简便方法

            Func func1 = new Func(calculator.Add);//泛型委托
            Func func2 = new Func(calculator.Sub);

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

            z = func1.Invoke(x, y);//func1(x, y);
            Console.WriteLine(z);
            z = func2.Invoke(x, y);//func2(x, y);
            Console.WriteLine(z);

        }
    }

 
    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;
        }

    }

3、委托的声明(自定义委托)

  • 委托是一种类(class),类是数据类型所以委托也是一种数据类型(引用类型)
  Type t = typeof(Action);
  Console.WriteLine(t.IsClass);
  • 它的声明方式与一般的类不同,主要是为了照顾可读性和 C/C++ 传统
  • 注意声明委托的位置
    • 避免写错地方结果声明成嵌套类型(在类里,声明委托属于嵌套)
  • 委托与所封装的方法必需“类型兼容”
    delegate double Calc(double x, double y);
            double Add(double x, double y) { return x + y; }
            double Sub(double x, double y) { return x - y; }
            double Mul(double x, double y) { return x * y; }
            double Div(double x, double y) { return x / y; }
- 返回值的数据类型一致
- 参数列表在个数和数据类型上一致(参数名不需要一样)

二、委托的一般使用

  • 实例:把方法当作参数传给另一个方法(函数式编程)
    • 正确使用1:模板方法,“借用”指定的外部方法来产生结果
      • 相当于“填空题”
      • 常位于代码中部
      • 委托有返回值
    • 正确使用2:回调(callback)方法,调用指定的外部方法
      • 相当于“流水线”
      • 常位于代码末尾
      • 委托无返回值
 class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory();
            WrapFactory wrapFactory = new WrapFactory();

            Func func1 = new Func(productFactory.MakePizza);
            Func func2 = new Func(productFactory.MakeToyCar);

            Logger logger = new Logger();
            Action log = new Action(logger.Log);

            Box box1 = wrapFactory.WrapProduct(func1, log);
            Box box2 = wrapFactory.WrapProduct(func2, log);

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

        }
    }

    class Logger
    {
        public void Log(Product product)
        {
            Console.WriteLine("Product '{0}' create 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 getProduct,Action 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 = "Ty Car";
            product.Price = 100;
            return product;
        }
    }
  • 注意:难精通+易使用+功能强大东西,一旦被滥用则后果非常严重
    • 缺点1:这是一种方法级别的紧耦合,现实工作中要慎之又慎
    • 缺点2:是可读性下降、debug 的难度增加
    • 缺点3:把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
    • 缺点4:委托使用不当有可能造成内存泄漏和程序性能下降
      (实例方法,必定使用对象。使用委托,对象就不能被释放

面试官:你遇到的最烂的最难调试的代码有哪些?
答:委托的滥用

三、委托的高级使用

  • 1、多播(multicast)委托(间接同步调用)
  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();

            //单播委托
            //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);
            }
        }
    }
   
  • 2、隐式异步调用

    • 同步与异步的简介
      • 中英文的语言差异
      • 同步:你做完了我(在你的基础上)接着做
      • 异步:咱们两个同时做(相当于汉语中的“同步进行”)
    • 同步调用与异步调用的对比
      • 每一个运行的程序是一个进程(process)
      • 每个进程可以有一个或者多个线程(thread)
      • 同步调用是在同一线程内
      • 异步调用的底层机理是多线程
      • 串行 == 同步 == 单线程,并行 == 异步 == 多线程
    • 隐式多线程 v.s. 显式多线程
      • 直接同步调用:使用方法名
      • 间接同步调用:使用单播放/多播委托的 Invoke 方法
      • 隐式异步调用:使用委托的 BeginInvoke
      • 显式异步调用:使用 Thread 或 Task
  • 3、应该适时地使用接口(interface)取代一些对委托的使用

    • Java 完全地使用接口取代了委托的功能,即 Java 没有与 C# 中委托相对应的功能实体
  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 };
            //直接同步调用
            stu1.DoHomework();
            stu1.DoHomework();
            stu1.DoHomework();
            //----------------

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

            action1.Invoke();
            action2.Invoke();
            action3.Invoke();
            //----------------
            //隐式异步调用
            action1.BeginInvoke(null, null);
            action2.BeginInvoke(null, null);
            action3.BeginInvoke(null, null);
            //---------------
            //显示异步调用
            //第一种方式
            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();

            //第二种方式
            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 < 5; i++)
            {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Main Thread {0}.", i);
                Thread.Sleep(1000);
            }

            Console.ReadKey();
        }
    }

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

你可能感兴趣的:(CSharp,c#)