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

2020-12-24 17:26

阅读:485

标签:div   log   使用   结果   自定义事件   任务   mouse   定义   类的构造函数   

一、委托

基本用法:

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参数 ,可以指定应运行的代码,这类似于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和Func委托。
      泛型Action委托表示引用一个void返回类型的方法。Action委托类存在不同的变体,可以传递至多16种不同的参数类型,没有泛型参数的Action类可以调用没有参数的方法。例如:Action调用带一个参数的方法,Action调用带两个参数的方法等
      Func的用法和Action用法类似,但是Func表示引用一个带返回类型的方法,Func也存在不同的变体,至多可以传递16个参数类型和1个返回类型,例如:Func表示带一个参数的方法,Func表示调用带两个参数的方法。
    下面就直接给一个Action和Func的例子

 

技术图片
技术图片
using System;
namespace DelegateFuncAction
{
    class Program
    {
        static void Main(string[] args)
        {
            Func DoAddtion = calculate.addtion;
            double result = DoAddtion(20, 30);
            Console.WriteLine("Func带返回参数委托做加法结果为:{0}",DoAddtion(10,20));
            calculate c=new calculate();
            Action 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);
        }
    }
}
 
 
本文转自:https://www.cnblogs.com/LCLBook/p/11697210.html


评论


亲,登录后才可以留言!