zoukankan      html  css  js  c++  java
  • C# 委托,事件, 异步

    委托

    ​ 委托可以说是方法的集合,里面放着方法列表,当调用的时候就会调用全部的方法列表

    ​ 个人理解 :

    • 当声明和创建时委托时, 它是个 对象
    • 当使用委托,调用方法时,它是个 方法

    声明委托类型

    delegate void MyDel(int x)
    

    对比一下filed字段声明

    int  count = 5;
    

    ​ 可以知道delegate 对应 int , 它是 种类型,void MyDel( int x ) 对应count

    创建对象

    private delegate void MyDel(int x);
    MyDel requestHandler =new MyDel(addCount);
    
    
    private void addCount(int x){
    	x+=x;
    }
    

    为委托类型里面添加方法

    requestHandler += addCount1;
    requestHandler += addCount2;
    //调用
    requestHandler(5);
    
    private void addCount1(int x){
      
    }
    private void addCount2(int x){
      
    }
    

    ​ 委托类型要是有返回值,那么最后一个方法执行的返回值就是就结果

    委托方法内存放的是匿名方法

    ​ 委托里的匿名方法的作用是初始化委托,就是方法列表中的方法是没有名字的

    MyDel requestHandler;
    requestHandler = delegate(int x){
        x+=x;
    }
    

    匿名方法捕获变量

    ​ 从这里也可以知道匿名方法的声音应该是与int y =5 ;同级或是比它低层次

    MyDel requestHandler;
    int y=5;
    requestHandler = delegate(int x){
    	x=y+5;
    }
    

    委托 + lamb表达式

    MyDel requestHandler;
    MyDel requestHandler1;
    //匿名方法
    requestHandler = delegate(int x) {x+20;}
    requestHandler1 =  x => {x+20;}
    

    事件

    ​ 联想一下观察者模式,当某一状态触发的时候,就会触发一些对象的方法,这个过程就像是委托一样调用方法列表一样,发布者就是一个委托对象,订阅者就是在里面的方法列表. 和方法,属性一样,事件event 是类或结构的成员,所以我们不能在一段可执行代码中声明事件,事件的初始值为null

    ​ 个人理解:

    • event 的声明为事件的关键字,和方法,属性,字段平级

    • 当在调用时事件就是调用方法

      ​对于事件的私有委托, 需要注意的事项:

    直接上代码

    delegate void CountDel();
    class Incrementer {
      //事件本身就是个观察者,一旦被触发,就马上通知订阅者
       public event CountDel  MyEvent;
      
      //这是某个触发点
      public void DoCount (){
        for(int i =1;i<100;i++){
          if(i%12==0 && MyEvent!=null){
               MyEvent();
          }
        }
      }
    
    }
    
    //订阅者
    class Dozens{
       public int DozenCount {get;private set}
    
      public void Dozens(Incrementer incrementer){
           incrementer +=AddDozenCount;
      }
      
      public void AddDozenCount(){
           DozenCount++;
      }
    }
    
    class Test {
       Incrementer inc = new Incrementer();
       Dozens dozens = new Dozens(inc);
       inc.DoCount();
    }
    

    标准事件(EventHandler)

    ​ .NET 环境中定义的标准事件,定义如下:

    public event delegate EventHandler (object sender , EventArgs args)
    

    ​ 从形参名字可以看出第一个参数表示发送者,第二个参数传递一些信息

    通过扩展EventArgs 来传递数据

    ​ 需要配置泛型委托来实现,上代码

    //自定义EventArgs
    class MyEventArgs : EventArgs{
       public int Anum{
         get;set;
       }
    }
    
    
    
    class Incrementer {
      //事件本身就是个观察者,一旦被触发,就马上通知订阅者
       public event EventHandler<MyEventArgs>  MyEvent;
      
      //这是某个触发点
      public void DoCount (){
        MyEventArgs eventArgs = new MyEventArgs();
        for(int i =1;i<100;i++){
          if(i%12==0 && MyEvent!=null){
               eventArgs.Anum = i;
               MyEvent(this,eventArgs);
          }
        }
      }
    
    }
    
    //订阅者
    class Dozens{
      public int DozenCount {get;private set}
    
      public void Dozens(Incrementer incrementer){
           incrementer +=AddDozenCount;
      }
      
      public void AddDozenCount(object obj,MyEventArgs e){
           Console.writeLine("当数到{0}时,增加一",e.Anum);
           DozenCount++;
      }
    }
    
    class Test {
       static void main (){
          Incrementer inc = new Incrementer();
          Dozens dozens = new Dozens(inc);
          inc.DoCount();
          Console.writeLine("共记录了{0}次",dozens.DozenCount);
       }
    }
    

    异步

    ​ 异步是什么 ? 例如你去买奶茶,店员说请稍等,你在店员做奶茶的同时,你可以刷刷朋友圈,做其他事,等奶茶做好了,你再付钱,完成了买奶茶这件事。对于我来说,店员调配奶茶就是一个异步方法。异步不一定就是在其他线程执行,也可能在同个线程执行

    ​ 首先看一下异步方法

    class Today{
        async Task<Tea> MakeTeaAsync (){
        	Console.Writeline("开始做奶茶呀!");
      		Salewoman girl = new Salewomen();
        	Tea tea = await girl.makeTea();
        	return tea;
         }
    }
    
    class Test {
      public static void main(string args){
        	Today  to = new Today();
        	Task<Tea> teaTask = to.MakeTeaAsync();
        	Console.writeLine(teaTask.Tostring());
      }
        
    }
    

    ​ 异步方法有三种返回值void,Task<T>,Task ,获取异步方法的结果调用Task.Result.调用方法的接收都是用Task 对象接收.

    ​ 如果我们的异步不需要异步的数据,那么可以一直执行下去,但是当并行的线程需要异步的数据仍然会阻塞等待

    class Test{
        public static void main (){
             Task<int> task = CountAsync();
                     ...
             //下面需要用到异步的数据,那么加入异步没完成,此处会阻塞
             Console.wrintLine("结果为: "+task.Result);
          
          	//异步方法另外两种返回值
            Task task = CounterAsync1();
            Task task = CounterAsync2();//或直接调用异步方法 CounterAsync();
        }
    }
    
    private async Task<int> CountAsync(){
       int result = await SocketHelper.get();
       return result;
    }
    
    private async Task CountAsync1(){
      	int result = await SocketHelper.getAgain();
      	Console.writeLine("hello world !");
    }
    
    private async void CountAsync2(){
        int result = await SocketHelper.getAgain1();
    }
    

    ​ 那么有没有,解决方法呢?我就是不想阻塞. 有, 回调.稍后介绍

    异步方法在新线程执行

    ​ 异步默认情况下是当前线程执行的,当需要新开线程执行时,直接使用lamb 表达式

    private async Task<int> CountAsync(){
      
       int result = await Task.Run(new Fun<int>(Get));
       //lamb表达式,
       int result1 = await Task.Run(()=>{
           return SocketHelper.get(num);
       });
      
       return result;
    }
    
    private int GetCount(int num){
        return SocketHelper.get(num);
    }
    

    Task.Run( ) 返回的结果有TaskTask<TResult>两种,形参用到的类有Action , Fun<TResult>Fun<Task>,其中Fun<Task>不讲,详细方法如下 , (学习两种简单的)

    public static Task Run (Action action) {}
    
    public static Task<TResult> Run (Func<TResult> func) {}
    

    ActionFunc<TResult> 是两个委托, 它们的方法列表签名是不需要参数,返回值一个为void ,另外一个为传入的泛型,上面的例子

    //委托Action 
     public delegate void Action();
    
    //委托Func<TResult>
    public delegate TResult Func<out TResult>();
    
    //创建匿名委托对象(方法列表)内添加Get方法
    int result = await Task.Run(new Fun<int>(Get));
    

    ​ 假如我需要个执行的任务是需要传递参数的,那么可以这样写

    int reusult = await Task.Run(()=>Get(5,6));
    

    ​ 上面的例子()=>Get(5,6)是创建了Func<TResult>对象,并且为这个委托(方法列表)内添加了一个匿名的不需参数的方法,在这个匿名方法内执行Get(5,6)

    异步方法中取消任务

    ​ 同样的是Task.Run( )方法,这里用到两个类,CancelationTokenCancelationTokenSource ,后者从名字可以看出是产生CancelationToken的,而使用CancelationToken去取消任务实际是令CancelationToken的属性IsCancelationRequested true

    public static Task Run (Action action , CancelationToken token) {}
    
    public static Task<TResult> Run (Func<TResult> func , CancelationToken token ) {}
    
    例子
    
    class Test {
      public void main(){
         CancelationTokenSource source = new CancelationTokenSource();
         CancelationToken tokent = source.token;
          
         MyClass my = new MyClass();
         Task task = my.CancelAsync(token);
          
         //task.wait();
         //token.cancel;
         task.wait();
         Console.writeLine("结束");
      }
    }
    
    class MyClass {
      public async void CancelAsync(CancelationToken token){
        if(IsCancelationRequested)
                return;
        Task task = await Task.Run(new  Fun(TestTask),token);
        
      }
      
      public  void TestTask(){
         Console.writeLine("开始");
         for(int i = 0 ; i < 5 ; i++){
             if(IsCancelationRequested){
                 return;
              }
                
             Thread.sleep(1000);
             Console.writeLine("{0} of counts is completed",i+1);
              	
         }
      }
    } 
    
    //带注释运行
    开始
    1 of counts is completed 
    2 of counts is completed
    3 of counts is completed
    4 of counts is completed 
    5 of counts is completed
    结束
      
    //不带注释运行
    开始
    1 of counts is completed 
    2 of counts is completed
    3 of counts is completed
    结束
    

    异步中处理异常

    await语句中直接加上try..catch

    等待异步完成后再继续运行

    //等待单个任务完成后继续执行
    Task.Wait()
      
    //等待一组任务完成后
    Task[] tasks = new Task[]{t1,t2};
    Task.WaitAll(tasks);
    

    委托异步编程

    ​ 当委托中只有一个方法(方法列表里只要一个方法)时可以使这个方法在另外的线程(线程池中的线程)中异步执行,使用的方法是委托的BeginInvoke ,当希望获取到异步的结果时可以检查BeginInvoke返回的IAsyncResultIscompleted属性,或是调用委托的EndInvoke来等待异步的完成.有三种异步编程方式。分别是轮询, 一直等待和回调。

    namespace Common.Been
    {
    
    
        internal delegate int MyDelegate(string milk, string tea);
        class Test
        {
            private MyDelegate del = delegate(string milk, string tea)
            {
                Console.WriteLine(milk + " " + tea);
                return milk.Length + tea.Length;
            };
    
    
            public void MyCallback(IAsyncResult asyncResult)
            {
               	AsyncResult AsyncCallback = (AsyncResult) asyncResult.AsyncState;
                MyDelegate callDelegate = (MyDelegate) AsyncCallback.AsyncDelegate;
                int result = callDelegate.EndInvoke(asyncResult);
                //这里在进行一些其他操作
               
            }
            public void main()
            {
                string m = "cow milk";
                string t = "black tea";
    
                //public IAsyncResult BeginInvoke(string milk, string tea, AsyncCallback callback, object state) 
                //不使用回调的情况
                IAsyncResult AsyncResult = del.BeginInvoke(m, t, null, null);
                int result = del.EndInvoke(AsyncResult);
    
                //public delegate void AsyncCallback(IAsyncResult ar);
                //使用回调
                IAsyncResult AsyncResult1 = del.BeginInvoke(m, t, new AsyncCallback(MyCallback), del);
                
    
            }
        }
    
    }
    

    BeginInvoke返回一个IAsyncResult对象,它实际上是个AsyhcResult对象,他们的关系是怎么样的呢?

    ​ 其中,AsyncDelegate保持着调用BeginInvoke方法的委托引用,AsyncState保存着是BeginInvoke方法的第四个参数.

    计时器

    ​ 周期执行的异步类,同样它也是从线程池内拿取线程执行方法,同时可以传入回调

    class Test {
        ///
      	///第二个参数: 传给回调的对象引用
        ///第三个参数: 多久后开始
        ///第四个参数: 每过多久循环
        public void main(){
          		//public Timer( TimerCallback callback, object state, int dueTime, int period) 
                //public delegate void TimerCallback(object state);
                string giveCallback = "love you ";
                Timer timer = new Timer(new TimerCallback(TimeCallback), giveCallback, 2000, 3000);
          
        }
      
        private void  TimeCallback(object obj)
        {
                string whatYouGive = (string) obj;
        }
    }
    
  • 相关阅读:
    用TextKit实现表情混排
    IOS类似9.png
    iphone 弹出键盘,文本框自动向上移动。
    IOS截取部分图片
    IOS给图片增加水印(图片、文字)
    iOS界面设计切图小结
    table tr th td thead tbody tfoot
    怎样让table没有边框
    margin、padding,border默认值
    Java 随机数
  • 原文地址:https://www.cnblogs.com/Benjious/p/8324990.html
Copyright © 2011-2022 走看看