• C# 委托 、事件、同步、异步知识点归纳


    一、委托

    基本用法:

    1.声明一个委托类型。委托就像是‘类'一样,声明了一种委托之后就可以创建多个具有此种特征的委托。(特征,指的是返回值、参数类型)

    public delegate void SomeKindOfDelegate(string result);

    2.创建一个在1中创建的委托类型的委托。

    public SomeKindOfDelegate aDelegate;

    3.为2中创建的具体的委托添加响应函数。响应函数必须符合1中的‘特征'。

    aDelegate +=new SomeKindOfDelegate(aFunctionThatJustForDelegate);
    private void aFunctionThatJustForDelegate(string result)
    {
      MessageBox.Show(result);
    }

    4.完成以上三步之后,就可以使用Invoke来对委托进行调用了。Invoke可以选择调用的目标函数,调用优先级,以及调用的参数。

    aDelegate.BeginInvoke("Hello~I'm being invoked!", null, null);

    上面是基本用法,除了这种基本用法之外,还可以结合var、匿名委托、lambda委托等方法。

    完整代码:

    复制代码
    namespace wtfIsDelegate
    {
     public delegate void SomeKindOfDelegate(string result);
     public partial class Form1 : Form
     {
      public event SomeKindOfDelegate aDelegate;
      public Form1()
      {
       InitializeComponent();
       aDelegate +=new SomeKindOfDelegate(aFunctionThatJustForDelegate);
       aDelegate.BeginInvoke("Hello~I'm being invoked!", null, null);
      }
      private void btnDelegate_Click(object sender, EventArgs e)
      {
      }
      private void aFunctionThatJustForDelegate(string result)
      {
       MessageBox.Show(result);
      }
     }
    }
    复制代码

    委托的用处:

    委托的优点,是可以实现异步(BeginInvoke),还可以在某种需要同时调用多个同参数、返回值的情况下简化代码。

    二、事件

    基本用法:

    1.定义委托。

    public delegate void SomeKindOfDelegate(string result);

    2.定义事件。

    public event SomeKindOfDelegate aDelegate;

    3.为事件添加响应函数。

    process.Exited += new EventHandler(CmdProcess_Exited);

    4.为事件规定触发(调用)方式。(【也可以没有触发方式,直接invoke】)

    解说:

    C#里,每一种‘事件Event'大概都对应着其‘事件处理者EventHandler'。比如Process类的OutputDataReceived事件对应着DataReceivedEventHandler,对于非特异性的‘事件',比如PasswordChanged 这种,它们统一都对应着RoutedEventHandler或者EventHandler这种较为通用的‘事件处理者'。然而,‘EventHandler'也只是充当了一个中介的角色,真正触发了‘Event'之后要做什么,还需要我们手动指定,像这样:

    process.Exited += new EventHandler(CmdProcess_Exited); // 注册进程结束事件 。

    EventHandler本来也是委托。比如

    public delegate void DataReceivedEventHandler(object sender, DataReceivedEventArgs e);

    自定义事件

    自定义事件是一种类似于委托的方式,

    自定义事件某种意义上改变了程序的流程,使得某个条件的变化由‘不断查询'转变为‘订阅与处理'的关系。

    自定义事件需要有以下几个元素:

    事件的发起者,事件的订阅,以及事件的处理程序。从发起者到处理程序之间是可以传参数的。

    一个事件的‘发起'可以依赖于某种系统消息,比如‘OnKeyDown'、‘OnMouseClick'(【我目前还没见过这么写的源码】),也可以在某个条件达成时(比如两次输入了同样的字符)自行调用(其实收到系统消息也算是‘条件达成')。【更多的event是这么写出来的】

    有些事件,并没有明显的‘发起者'。

    委托和事件是啥关系

    委托和自定义事件的使用方式十分十分类似。event 只有类的内部可以 Invoke,delegate的话,在哪都可以Invoke。调用的方式貌似也略有区别(传参方式)

    由于调用方式和传参的差异,event显得更保守/稳定一些。event也更容易从‘理解'上更容易接受。

    delegate貌似更多的用于进行异步(begin invoke)。而event则更多的用来做自定义事件。

    委托和异步是啥关系

    异步是委托可以实现的一种功能(或者叫做‘现象'也可以) 异步可以由很多种其他方式体现,比如多线程(thread,threadpool,task等等)。

    多线程

        .Net的委托本质上就是指向函数的指针,只不过这种指针是经过封装后类型安全的。委托和线程是两个不同的概念,线程是动态的,委托就是一个或一组内存地址,是静态的。线程执行时如果遇到了指向函数的指针就执行这个函数。
    .Net为了方便编程,给委托赋予了两种方式以供调用线程来执行,即同步和异步方式,它们分别通过Invoke和BeginInvoke来开启。Invoke就是同步执行,由调用线程来执行,而BeginInvoke则开启了一个后台线程来执行delegate所指向的函数,这个后台线程和调用线程之间属于异步执行方式。实际上有了delegate这个概念,你在编程时就可以不用直接使用Thread类来开辟新的线程了,因为微软替你实现了。

        使用BeginInvoke调用委托方法,其结果和调用一个新线程一样。多线程编程在开发中经常用到,比如将后台计算和UI更新主线程分离,防止界面卡顿等,着重关注线程池ThreadPool,因为Task任务就是整理了它。

    Task

        .NET 4包含新名称空间System.Threading.Tasks,它 包含的类抽象出了线程功能。Task 在后台使用ThreadPool。 任务表示应完成的某个单元的工作。 这个单元的工作可以在单独的线程中运行,也可以以同步方式启动一个任务,这需要等待主调线程。 使用任务不仅可以获得一个抽象层,还可以对底层线程进行很多控制。 

        在安排需要完成的工作时,任务提供了非常大的灵活性。 例如,可以定义连续的工作—— 在一个任务完成后该执行什么工作。 这可以区分任务成功与否。 另外,还可以在层次结构中安排任务。例如,父任务可以创建新的子任务。 这可以创建一种依赖关系,这样,取消父任务,也会取消其子任务。
    启动一个Task

    要启动任务,可 以使用 TaskFactory类 或 Task类 的构造函数和 Start()方法。Task类的构造函数在创建任务上提供的灵活性较大。 
    在启动任务时,会创建Task类 的一个实例,利用Action或Action<object>委托不带参数或带一个object参数 ,可以指定应运行的代码,这类似于Thread类 。下面定义了一个无参数的方法。 在实现代码中,把任务的ID写入控制台中: 

    static void TaskMethod()
    {
        Console.WriteLine("running in a task");
        Console.WriteLine("Task id: {0}",Task.CurrentId);
    }

         在上面的代码中,可 以看到启动新任务的不同方式。第一种方式 使用实例化TaskFactory类 ,在其中把 TaskMedlod()方 法传递给StartNew()方法,就会立即启动任务。 第二种方式使用 Task类的构造函数。 实例化 Task对象时,任务不会立即运行,而是指定 Created状态。接着调用 Task类的Start()方法,来启动任务。 使用Task类 时,除了调用 Start()方法,还可以调用RunSynchronously()方法。这样,任务也会启动,但在调用者的当前线程中它正在运行,调用者需要一直等待到该任务结束。 默认情况下,任务是异步运行的。

    复制代码
        //using task factory
        TaskFactory tf = new TaskFactory();
        Task t1 = tf.StartNew(TaskMethod);
    
        //using the task factory via a task
        Task t2 = Task.TaskFactory.StartNew(TaskMethod);
    
        //using Task constructor
        Task t3 = new Task(TaskMethod);
        t3.Start();
    复制代码

           使用Task类的构造函数和TaskFactory类的StartNew()方法时,都可以传递TaskCreationOptions枚举中的值。设置LongRunning选项,可以通知任务调度器,该任务需要较长时间执行,这样调度器更可能使用新线程。如果该任务应关联到父任务上,而父任务取消了,则该任务也应取消,此时应设置 AuachToParent选项。PreferFairness的值表示,调度器应提取出已在等待的第一个任务。 如果一个任务在另一个任务内部创建,这就不是默认情况 。如果任务使用子任务创建了其他工作,子任务就优先于其他任务。 它们不会排在线程池队列中的最后。 如果这些任务应以公平的方式与所有其他任务一起处理,就设置该选项为PreferFairness。 

    Task t4 = new Task(TaskMethod, TaskCreationOptions.PreferFairness);
    t4.Start();

    Action,Func等委托

          泛型无返回值委托Action,有返回值Func,虽然使用Delegete委托可以达到同样的效果,但是用Action等泛型委托写法要更简洁。除了Delegate委托我们还可以使用Action<T>和Func<T>委托。
          泛型Action<T>委托表示引用一个void返回类型的方法。Action<T>委托类存在不同的变体,可以传递至多16种不同的参数类型,没有泛型参数的Action类可以调用没有参数的方法。例如:Action<in T1>调用带一个参数的方法,Action<in T1,in T2>调用带两个参数的方法等
          Func<T>的用法和Action<T>用法类似,但是Func<T>表示引用一个带返回类型的方法,Func<T>也存在不同的变体,至多可以传递16个参数类型和1个返回类型,例如:Func<in T1,out Resout>表示带一个参数的方法,Func<in T1,in T2,out Resout>表示调用带两个参数的方法。
        下面就直接给一个Action<T>和Func<T>的例子

    复制代码
    using System;
    namespace DelegateFuncAction
    {
        class Program
        {
            static void Main(string[] args)
            {
                Func<double, double,double> DoAddtion = calculate.addtion;
                double result = DoAddtion(20, 30);
                Console.WriteLine("Func带返回参数委托做加法结果为:{0}",DoAddtion(10,20));
                calculate c=new calculate();
                Action<double, double> DoSubstraction = c.substraction;
                DoSubstraction(90, 20);
            }
        }
        class calculate
        {
            public static double addtion(double x, double y)
            {
                return x + y;
            }
            public void substraction(double x, double y)
            {
                Console.WriteLine("Action不带返回参数委托做减法结果为:{0}",x-y);
            }
        }
    }
    复制代码


  • 相关阅读:
    鸡兔同笼问题
    猴子吃桃问题
    Fibonacci_sequence(斐波那契数列)
    Joseph_Circle(约瑟夫环)
    学生成绩管理--功能全--较难
    各种排序
    二叉排序树操作--基本
    面向对象程序设计(多继承)--简单
    面向对象程序设计3--简单
    使用 ASR 和 Azure Pack 为 IaaS 工作负荷提供托受管 DR
  • 原文地址:https://www.cnblogs.com/LCLBook/p/11697210.html
Copyright © 2020-2023  润新知