委托学习——4. 委托的高级使用

1. 多播(Multicast)委托:一个委托内部封装多个方法

 使用形式如下

action1 += action2;//action1先把action2封装
action1 += action3;//action1再把action2封装

//现在action1里面封装了3个方法,调用action1如下三个方法都会得到执行
action1.Invoke();//像这种一个委托封装多个方法的使用方式叫做多播委托,封装顺序等于执行顺序

多播委托属于同步调用,源代码如下:

using Microsoft.VisualBasic;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace DelegateExample
{
    
    //一个委托内部封装多个方法
    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};
            //创建3个委托
            Action action1 = new Action(stu1.DoHomeWork);
            Action action2 = new Action(stu2.DoHomeWork);
            Action action3 = new Action(stu3.DoHomeWork);

            //调用委托
            #region 单播委托
            //action1.Invoke();
            //action2.Invoke();
            //action3.Invoke();
            //以上这种一个委托封装一个方法的形式叫做单播委托
            #endregion

            #region 多播(multicast)委托

            action1 += action2;//action1先把action2封装
            action1 += action3;//action1再把action2封装

            //现在action1里面封装了3个方法,调用action1如下三个方法都会得到执行
            action1.Invoke();//像这种一个委托封装多个方法的使用方式叫做多播委托
            //封装顺序等于执行顺序

            #endregion
        }
    }

    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);//在哪个线程调用这个sleep,这个线程就歇逼一秒钟
            }
        }

    }

}


结果如下:
 

此处留意下,三个学生的颜色都是不一样的

 

2.  隐式异步调用

 同步与异步的区别

同步:你做完了我在你的基础上继续做
以下三个方法依次运行,一个运行完了才会运行才一个

            #region 直接同步调用

            //且这三个方法都在主线程内调用
            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(100);//在哪个线程调用这个sleep,这个线程就歇逼一秒钟
            }
            #endregion

 间接同步调用:

            //间接同步调用
            Action action1 = new Action(stu1.DoHomeWork);
            Action action2 = new Action(stu2.DoHomeWork);
            Action action3 = new Action(stu3.DoHomeWork);
            //尽管是间接调用,但是还是同步的,一行行代码运行
            action1.Invoke();
            action2.Invoke();
            action3.Invoke();


异步:咱俩同时开始做
 

 使用委托进行的隐式异步调用,刚才我么已经知道使用Invoke是间接同步调用,现在使用BeginInvoke则可以进行隐式异步调用

//异步调用
            action1.BeginInvoke(null, null);//会在一个分支线程调用action1的方法
            //BeginInvoke参数一是异步调用的回调,指的是在分支线程里运行完action1以后继续执行的动作,null就是执行完action1啥也不干
            //参数二默认null

            action2.BeginInvoke(null, null);
            action3.BeginInvoke(null, null);

结果如下:
 

        首先,此时的顺序已经乱了,这个顺序是由分配的线程决定的,主线程和分支线程在同时运行;其次,颜色发生了混乱变化,如前三行结果颜色都一样,这是因为他们同时访问前景色,发生了冲突 

3. 显式异步调用

1. 使用Thread的显式异步调用

 //显示异步调用
            //方式一:使用Thread创建三个线程(比较老的使用方式)
            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();

结果如下:

可以看到与刚才的隐式异步调用是差不多的,而且也发生了冲突

2. 使用Task的显式异步调用(推荐)

//方式二:使用Task创建三个线程(推荐使用此方式)
            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();

 结果如下:

 结果同上,仍然是发生了资源上的冲突

隐式和显示异步调用的完整代码如下:
 

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

namespace 委托的高级使用1
{
    
    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 };


            #region 隐式异步调用
            //Action action1 = new Action(stu1.DoHomeWork);
            //Action action2 = new Action(stu2.DoHomeWork);
            //Action action3 = new Action(stu3.DoHomeWork);

            //action1.BeginInvoke(null, null);//会在一个分支线程调用action1的方法
            BeginInvoke参数一是异步调用的回调,指的是在分支线程里运行完action1以后继续执行的动作,null就是执行完action1啥也不干
            参数二默认null
            //action2.BeginInvoke(null, null);
            //action3.BeginInvoke(null, null);
            #endregion


            #region 显示异步调用
            方式一:使用Thread创建三个线程(比较老的使用方式)
            //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创建三个线程(推荐使用此方式)
            //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();

            #endregion



            for (int i = 0; i < 10; i++)
            {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine("Main thread {0} ", i);
                Thread.Sleep(100);//在哪个线程调用这个sleep,这个线程就歇逼一秒钟
            }

            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(100);//在哪个线程调用这个sleep,这个线程就歇逼一秒钟
            }
        }

    }

}

4. 应当适时的使用接口(interface)取代一些对委托的使用 

Java完全使用接口来代替了委托的功能,即Java没有与C#中委托相对应的功能实体 

用之前的模板方法的程序做个例子,请按照我的注释顺序去看:
 

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

namespace _5.应用接口取代委托
{
    internal class Program
    {
        static void Main(string[] args)
        {
            //8. 经过7的步骤以后下面这行会出错,我们的目标是把这个错误解决掉,首先,我们去重构一下模板方法
            //ProductFactory productFactory = new ProductFactory();

            //9. 创建新实例
            IProductFactory pizzaFactory = new PizzaFactory();
            IProductFactory toycarFactory= new ToyCarFactory();

            WrapFactory wrapFactory = new WrapFactory();

            //Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
            //Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);

            //10. 把两个工厂传进去,此时程序之中已经没有委托的身影了
            Box box1 = wrapFactory.WrapProduct(pizzaFactory);
            Box box2 = wrapFactory.WrapProduct(toycarFactory);

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

            Console.ReadKey();

        }
    }

    //1. 首先,跟着佩奇叔叔声明一个接口
    interface IProductFactory
    {
        Product Make();
    }

    //2. 其次,跟着佩奇叔叔再生两个类
    class PizzaFactory : IProductFactory//这是一个披萨工厂,这个类实现了IProductFactory接口
    {
        //我们把鼠标点在上面的IProductFactory,并且同时按下Alt和Enter,选择实现接口就会生成下面的Make()方法
        public Product Make()
        {
            //throw new NotImplementedException();//默认生成的这行代码我们不要

            //从原来的披萨工厂剪切过来的代码
            Product product = new Product();
            product.Name = "Pizza";
            return product;
            //4. 所谓重构,就是基本不改变原来的代码,只是把它放在更合适的地方
        }
    }

    //5. 跟着佩奇叔叔生成第二个类
    class ToyCarFactory : IProductFactory
    {
        public Product Make()
        {
            //throw new NotImplementedException();

            Product product = new Product();
            product.Name = "ToyCar";
            return product;
        }
    }


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

    class Box
    {
        public Product Product { get; set; }
    }

    class WrapFactory
    {
        //9. 现在我们的模板方法已经不需要委托类型的参数了,而是一个工厂类型的参数
        public Box WrapProduct(IProductFactory productFactory)
        { 
            Box box = new Box();
            Product product = productFactory.Make();//这里对应更改
            box.Product = product;
            return box;
        }
    }


    //7. 把东西都挪走以后这个类就没用了,我们把它注释掉
    //class ProductFactory
    //{
    //    //3. 我们把原来这个工厂的东西剪切到刚才的接口中去
    //    public Product MakePizza()
    //    {
           
    //    }

    //    //6. 同上
    //    public Product MakeToyCar()
    //    {
           
    //    }

    //}

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值