C艹——委托

什么是委托

委托(delegate)是函数指针的升级版

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

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

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

·委托的简单使用
Action委托
Func委托

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp33
{
    class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Action action = new Action(calculator.Report);///只需要Report方法名,不需要调用这个函数所以后面不加()。此时action已经指向calculator.Report这个方法
            calculator.Report();///直接调用
            action.Invoke();///间接调用
            action();///简单写法

            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;

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

            z = func2.Invoke(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;
        }
    }
}


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

·委托是一种类(class),类是数据类型所以委托也是数据类型
·它的声明方式与一般的类不同,主要是为了照顾可读性和C/C++传统
·注意声明委托的位置
避免写错地方声明成嵌套类型
·委托与所封装的方法必须“类型兼容”
返回值的数据类型一致
参数列表在个数和数据类型上一致(参数名不需要相同)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp34
{
    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;

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




        }
    }

    class Calculator
    {
        public double Add(double x, double y)
        {
            return x + y;
        }

        public double Sub(double x, double y)
        {
            return x - y;
        }

        public double Mul(double x, double y)
        {
            return x * y;
        }

        public double Div(double x, double y)
        {
            return x / y;
        }
    }
}

委托的一般使用

·实例:把方法当作参数传给另一个方法
正确使用1:模板方法,“借用”指定的外部方法来产生结果
·相当于“填空题”
·常位于代码中部
·委托有返回值

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp35
{
    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(box1.Product.Name);
            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();///准备一个Box
                Product product = getProduct.Invoke();///获取产品
                box.Product = product;///把产品装到盒子里
                return box;   ///返回Box  
               ///模板方法的优点
               ///Product类、Box类、WrapFactory类都不用动,只需拓展ProductFactory的方法即可
               ///这样就实现了Reuse,重复使用,也叫“复用”。代码的复用不但可以提高工作效率,还可以减少BUG的引入。良好的结构复用结构是优秀软件所追求的共同目标之一。
            }
        }

        class ProductFactory
        {
            public Product MakePizza()
            {
                Product product = new Product();
                product.Name = "pizza";
                return product;
            }

            public Product MakeToyCar()
            {
                Product product = new Product();
                product.Name = "Toy car";
                return product;
            }
        }
    }
}

正确使用2:回调(call back)方法,调用指定的外部方法
·相当于流水线
·常位于代码末尾
·委托无返回值

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp35
{
    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(box1.Product.Name);
            Console.WriteLine(box2.Product.Name);
        }

        class  Logger//记录程序的运行状态,一般没有返回值
        {
            public void Log(Product product)
            {
                Console.WriteLine("Product'{0}'created 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)///不带返回值的方法用Action,Log接受一个Prodduct类型的参数
            {
                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 = "Toy car";
                product.Price = 100;
                return product;
            }
        }
    }
}

小结:

·无论是模板方法还是回调方法他们的本质都是一样的。都是用委托类型的参数,封装一个外部的方法,然后把这个方法传进方法内部,再进行间接调用。

注意:难精通+易使用+功能强大的东西,一旦被滥用则后果非常严重
·缺点1:这是一种方法级别的紧耦合,现实工作中要慎之又慎
·缺点2:使可读性下降,debug难度增加
·缺点3:把委托回调,异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
·缺点4:委托使用不当有可能造成内存泄漏和程序性能下降

委托的高级使用

·单播委托

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleApp36
{
    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 < 6; i++)
        {
            Console.ForegroundColor = this.PenColor;
            Console.WriteLine("Student{0}doing homework {1}  hour(s)", this.ID, i);
            Thread.Sleep(1000);///1000毫秒=1秒,这个意思是无论你在哪个线程调用Thread.sleep(1000),这个线程就睡上一秒钟
        }
    }
}



·多播(multicast)委托(同步调用)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleApp36
{
    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 += action3;///action2封装在action1里    
            action1 += action2;///action3也封装在action1里,action1里面封装了3个方法

            action1.Invoke();///像这种有个委托封装多种方法的就叫多播委托
            ///执行顺序按照你的封装顺序执行
      
        }

    }
}

class Student
{
    public int ID { get; set; }
    public ConsoleColor PenColor { get; set; }

    public void DoHomeWork()
    {
        for (int i = 0; i < 6; i++)
        {
            Console.ForegroundColor = this.PenColor;
            Console.WriteLine("Student {0} doing homework {1}  hour(s)", this.ID, i);
            Thread.Sleep(1000);///1000毫秒=1秒,这个意思是无论你在哪个线程调用Thread.sleep(1000),这个线程就睡上一秒钟
        }
    }
}

`隐式异步调用

`同步与异步的简介
·中英文的语言差异
同步:你做完了我(在你的基础上)接着做
异步:咱们两个同时做(相当于汉语中的同步进行)

·同步调用与异步调用的对比
每一个运行的程序是一个进程(process)
每个进程可以有一个或者是多个线程(thread)
同步调用时在统一线程内
在这里插入图片描述
第一种同步调用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleApp36
{
    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();///调用方法
            stu2.DoHomeWork();
            stu3.DoHomeWork();
            ///调用完毕以后主线程还有事情要做,典型的同步调用
            for (int i = 0; i < 10; 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 < 6; i++)
        {
            Console.ForegroundColor = this.PenColor;
            Console.WriteLine("Student {0} doing homework {1}  hour(s)", this.ID, i);
            Thread.Sleep(1000);///1000毫秒=1秒,这个意思是无论你在哪个线程调用Thread.sleep(1000),这个线程就睡上一秒钟
        }
    }
}

第二种同步调用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleApp36
{
    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();
            ///单播间接同步调用
            ///
            /*
             action1 += action2;
             action1 += action3;
             ///多播间接同步调用
             */
            for (int i = 0; i < 10; 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 < 6; i++)
        {
            Console.ForegroundColor = this.PenColor;
            Console.WriteLine("Student {0} doing homework {1}  hour(s)", this.ID, i);
            Thread.Sleep(1000);///1000毫秒=1秒,这个意思是无论你在哪个线程调用Thread.sleep(1000),这个线程就睡上一秒钟
        }
    }
}


异步调用的底层机理是多线程(线程互不相干)
在这里插入图片描述
在这里插入图片描述
隐式异步调用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleApp36
{
    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);///异步隐式调用,BeginInvoke会自动创建一个分支线程帮我们调用封装的方法,括号两个参数关于后续操作,不需要,所以填null
            action2.BeginInvoke(null, null);//属于委托的隐式异步调用
            action3.BeginInvoke(null, null);

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

            Console.ReadLine();


        }

    }
}

class Student
{
    public int ID { get; set; }
    public ConsoleColor PenColor { get; set; } 

    public void DoHomeWork()
    {
        for (int i = 0; i < 6; i++)
        {
            Console.ForegroundColor = this.PenColor;
            Console.WriteLine("Student {0} doing homework {1}  hour(s)", this.ID, i);
            Thread.Sleep(1000);///1000毫秒=1秒,这个意思是无论你在哪个线程调用Thread.sleep(1000),这个线程就睡上一秒钟
        }
    }
}

执行结果
在这里插入图片描述

显示异步调用(比较古老的方法)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleApp36
{
    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(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.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Main thread {0}.", i);
                Thread.Sleep(1000);
            }



            Console.ReadLine();


        }

    }
}

class Student
{
    public int ID { get; set; }
    public ConsoleColor PenColor { get; set; } 

    public void DoHomeWork()
    {
        for (int i = 0; i < 6; i++)
        {
            Console.ForegroundColor = this.PenColor;
            Console.WriteLine("Student {0} doing homework {1}  hour(s)", this.ID, i);
            Thread.Sleep(1000);///1000毫秒=1秒,这个意思是无论你在哪个线程调用Thread.sleep(1000),这个线程就睡上一秒钟
        }
    }
}
 

执行结果
在这里插入图片描述
显示异步调用(更高级的方法)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleApp36
{
    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 };

            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.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Main thread {0}.", i);
                Thread.Sleep(1000);
            }



            Console.ReadLine();


        }

    }
}

class Student
{
    public int ID { get; set; }
    public ConsoleColor PenColor { get; set; } 

    public void DoHomeWork()
    {
        for (int i = 0; i < 6; i++)
        {
            Console.ForegroundColor = this.PenColor;
            Console.WriteLine("Student {0} doing homework {1}  hour(s)", this.ID, i);
            Thread.Sleep(1000);///1000毫秒=1秒,这个意思是无论你在哪个线程调用Thread.sleep(1000),这个线程就睡上一秒钟
        }
    }
}

执行结果
在这里插入图片描述

串行同步单线程,并行同步多线程

·隐式多线程 v.s. 显示多线程
直接同步调用:使用方法名
间接同步调用:使用单播/多播委托的invoke方法
隐式异步调用:使用委托的BeginInvoke
显示异步调用:使用Thread或Task

`应该适时的使用一些接口( interface)取代一些对委托的使用

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp35
{
    class Program
    {
        static void Main(string[] args)
        {
            IProductFactory pizzaFactory = new PizzaFactory();
            IProductFactory toyCarFactory = new ToyCarFortory();

            WrapFactory wrapFactory = new WrapFactory();



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


            Box box1 = wrapFactory.WrapProduct(pizzaFactory, log);
            Box box2 = wrapFactory.WrapProduct(toyCarFactory, log);

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

        interface IProductFactory
        {
            Product Make();
        }

        class PizzaFactory : IProductFactory
        {
            public Product Make()
            {
                Product product = new Product();
                product.Name = "pizza";
                product.Price = 12;
                return product;
            }
        }

        class ToyCarFortory : IProductFactory
        {
            public Product Make()
            {
                Product product = new Product();
                product.Name = "Toy car";
                product.Price = 100;
                return product;
            }
        }

        class Logger
        {
            public void Log(Product product)
            {
                Console.WriteLine("Product'{0}'created 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(IProductFactory productFactory, Action<Product> logCallBack)///不带返回值的方法用Action
            {
                Box box = new Box();
                Product product = productFactory.Make();
                if (product.Price >= 50)
                {
                    logCallBack(product);
                }
                box.Product = product;
                return box;

            }
        }
    }
}

java完全使用接口取代的委托的功能,即Java没有与C艹中委托相对应的功能实体

·

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值