博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
c#async await_通过C#中的async / await来“也许” monad(无任务!)
阅读量:2511 次
发布时间:2019-05-11

本文共 26779 字,大约阅读时间需要 89 分钟。

c#async await

Generalized async return types — it is a new C#7 feature that allows using not only Task as a return type of async methods but also other types (classes or structures) that satisfy some specific requirements.

通用异步返回类型 -这是C#7的一项新功能,不仅允许将Task作为异步方法的返回类型使用,还可以使用满足某些特定要求的其他类型(类或结构)。

At the same time, async/await is a way to call a set of "continuation" functions inside some context which is an essence of another design pattern — Monad. So, can we use async/await to write a code which will behave in the same way like if we used monads? It turns out that — yes (with some reservations). For example, the code below is compilable and working:

同时, 异步/等待是一种在某些上下文内调用一组“连续”函数的方法,这是另一种设计模式( Monad )的本质。 因此,我们可以使用async / await编写代码,使其行为与使用monad时相同吗? 事实证明,是的(有些保留)。 例如,下面的代码是可编译的并且可以正常工作:

async Task Main(){  foreach (var s in new[] { "1,2", "3,7,1", null, "1" })  {      var res = await Sum(s).GetMaybeResult();      Console.WriteLine(res.IsNothing ? "Nothing" : res.GetValue().ToString());  }  // 3, 11, Nothing, Nothing}async Maybe
Sum(string input){ var args = await Split(input);//No result checking var result = 0; foreach (var arg in args) result += await Parse(arg);//No result checking return result;}Maybe
Split(string str){ var parts = str?.Split(',').Where(s=>!string.IsNullOrWhiteSpace(s)).ToArray(); return parts == null || parts.Length < 2 ? Maybe
.Nothing() : parts;}Maybe
Parse(string str) => int.TryParse(str, out var result) ? result : Maybe
.Nothing();

Further, I will explain how the code works...

此外,我将解释代码的工作原理...

广义异步返回类型 (Generalized async return types)

First of all let's figure out what is required to use our own type (e.g. MyAwaitable<T>) as a result type of some async function. Documentation says that such type has to have:

首先,让我们弄清楚使用我们自己的类型(例如MyAwaitable <T> )作为某些异步函数的结果类型需要什么。 文档说这种类型必须具有:

  1. GetAwaiter() method that returns an object of a type which implements INotifyCompletion interface and has bool IsCompleted property and T GetResult() method;

    GetAwaiter()方法,该方法返回一个实现INotifyCompletion接口且具有bool IsCompleted属性和T GetResult()方法的类型的对象;

  2. [AsyncMethodBuilder(Type)] attribute which points to a "method builder" class (or structure) e.g. MyAwaitableTaskMethodBuilder<T> with the following methods:

    [AsyncMethodBuilder(Type)]属性指向具有以下方法的“方法生成器”类( 或结构 ),例如MyAwaitableTaskMethodBuildBuilder <T>

    • static Create()

      静态Create()
    • Start(stateMachine)

      开始(stateMachine)
    • SetResult(result)

      SetResult(结果)
    • SetException(exception)

      SetException(异常)
    • SetStateMachine(stateMachine)

      SetStateMachine(stateMachine)
    • AwaitOnCompleted(awaiter, stateMachine)

      AwaitOnCompleted(awaiter,stateMachine)
    • AwaitUnsafeOnCompleted(awaiter, stateMachine)

      AwaitUnsafeOnCompleted(awaiter,stateMachine)
    • Task

      任务

这是MyAwaitable和MyAwaitableTaskMethodBuilder的简单实现 (Here is a simple implementation of MyAwaitable and MyAwaitableTaskMethodBuilder)

[AsyncMethodBuilder(typeof(MyAwaitableTaskMethodBuilder<>))]public class MyAwaitable
: INotifyCompletion{ private Action _continuation; public MyAwaitable() { } public MyAwaitable(T value) { this.Value = value; this.IsCompleted = true; } public MyAwaitable
GetAwaiter() => this; public bool IsCompleted { get; private set; } public T Value { get; private set; } public Exception Exception { get; private set; } public T GetResult() { if (!this.IsCompleted) throw new Exception("Not completed"); if (this.Exception != null) { ExceptionDispatchInfo.Throw(this.Exception); } return this.Value; } internal void SetResult(T value) { if (this.IsCompleted) throw new Exception("Already completed"); this.Value = value; this.IsCompleted = true; this._continuation?.Invoke(); } internal void SetException(Exception exception) { this.IsCompleted = true; this.Exception = exception; } void INotifyCompletion.OnCompleted(Action continuation) { this._continuation = continuation; if (this.IsCompleted) { continuation(); } }}public class MyAwaitableTaskMethodBuilder
{ public MyAwaitableTaskMethodBuilder() => this.Task = new MyAwaitable
(); public static MyAwaitableTaskMethodBuilder
Create() => new MyAwaitableTaskMethodBuilder
(); public void Start
(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine => stateMachine.MoveNext(); public void SetStateMachine(IAsyncStateMachine stateMachine) { } public void SetException(Exception exception) => this.Task.SetException(exception); public void SetResult(T result) => this.Task.SetResult(result); public void AwaitOnCompleted
( ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine => this.GenericAwaitOnCompleted(ref awaiter, ref stateMachine); public void AwaitUnsafeOnCompleted
( ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : ICriticalNotifyCompletion where TStateMachine : IAsyncStateMachine => this.GenericAwaitOnCompleted(ref awaiter, ref stateMachine); public void GenericAwaitOnCompleted
( ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine => awaiter.OnCompleted(stateMachine.MoveNext); public MyAwaitable
Task { get; }}

Now we can use MyAwaitable as a result type of async methods:

现在我们可以将MyAwaitable用作异步方法的结果类型:

private async MyAwaitable
MyAwaitableMethod(){ int result = 0; int arg1 = await this.GetMyAwaitable(1); result += arg1; int arg2 = await this.GetMyAwaitable(2); result += arg2; int arg3 = await this.GetMyAwaitable(3); result += arg3; return result;}private async MyAwaitable
GetMyAwaitable(int arg){ await Task.Delay(1);//Simulate asynchronous execution return await new MyAwaitable
(arg);}

The code works as expected but to understand a purpose of the requirements to MyAwaitable let's take a look what the C# preprocessor does with MyAwaitableMethod. If you run some de-compilation util (e.g. dotPeek) you will see that the original method was changed as follows:

该代码可以正常工作,但是要了解MyAwaitable需求的目的,让我们看一下C#预处理程序对MyAwaitableMethod所做的工作 。 如果您运行一些反编译工具(例如dotPeek),则会看到原始方法已更改如下:

private MyAwaitable
MyAwaitableMethod(){ var stateMachine = new MyAwaitableMethodStateMachine(); stateMachine.Owner = this; stateMachine.Builder = MyAwaitableTaskMethodBuilder
.Create(); stateMachine.State = 0; stateMachine.Builder.Start(ref stateMachine); return stateMachine.Builder.Task;}

MyAwaitableMethodStateMachine (MyAwaitableMethodStateMachine)

Actually, it is a simplified code where I omit a lot of optimizations to make a compiler generated code readable

实际上,这是一个简化的代码,在这里我省略了很多优化以使编译器生成的代码可读

sealed class MyAwaitableMethodStateMachine : IAsyncStateMachine{    public int State;    public MyAwaitableTaskMethodBuilder
Builder; public BuilderDemo Owner; private int _result; private int _arg1; private int _arg2; private int _arg3; private MyAwaitableAwaiter
_awaiter1; private MyAwaitableAwaiter
_awaiter2; private MyAwaitableAwaiter
_awaiter3; private void SetAwaitCompletion(INotifyCompletion awaiter) { var stateMachine = this; this.Builder.AwaitOnCompleted(ref awaiter, ref stateMachine); } void IAsyncStateMachine.MoveNext() { int finalResult; try { label_begin: switch (this.State) { case 0: this._result = 0; this._awaiter1 = this.Owner.GetMyAwaitable(1).GetAwaiter(); this.State = 1; if (!this._awaiter1.IsCompleted) { this.SetAwaitCompletion(this._awaiter1); return; } goto label_begin; case 1:// awaiter1 should be completed this._arg1 = this._awaiter1.GetResult(); this._result += this._arg1; this.State = 2; this._awaiter2 = this.Owner.GetMyAwaitable(2).GetAwaiter(); if (!this._awaiter2.IsCompleted) { this.SetAwaitCompletion(this._awaiter2); return; } goto label_begin; case 2:// awaiter2 should be completed this._arg2 = this._awaiter2.GetResult(); this._result += this._arg2; this.State = 3; this._awaiter3 = this.Owner.GetMyAwaitable(3).GetAwaiter(); if (!this._awaiter3.IsCompleted) { this.SetAwaitCompletion(this._awaiter3); return; } goto label_begin; case 3:// awaiter3 should be completed this._arg3 = this._awaiter3.GetResult(); this._result += this._arg3; finalResult = this._result; break; default: throw new Exception(); } } catch (Exception ex) { this.State = -1; this.Builder.SetException(ex); return; } this.State = -1; this.Builder.SetResult(finalResult); }}

Reviewing the generated code we can see that the "method builder" has the following responsibilities:

查看生成的代码,我们可以看到“方法生成器”具有以下职责:

  1. Scheduling the state machine MoveNext() method call when a child asynchronous operation is done (in the simplest scenario we just pass MoveNext() into OnCompleted() of the async operation awaiter).

    安排当孩子异步操作完成的状态机的MoveNext()方法调用(在最简单的情况下,我们只是通过异步操作awaiter的的MoveNext()OnCompleted())。

  2. Creation of an asynchronous operation context object (public MyAwaitable<T> Task { get; })

    创建异步操作上下文对象( public MyAwaitable<T> Task { get; } )

  3. Reacting on final states of generated state machines: SetResult or SetException.

    对生成的状态机的最终状态做出React: SetResultSetException

In other words, with "method builders" we can get a control on how asynchronous methods are executed and it looks like a feature which will help us to achieve our goal — an implementation of Maybe monad behavior. But what is good about that monad? Well… you can find a lot of articles about that monad in the Internet, so here I will describe just basics.

换句话说,以“法建设者”,我们可以得到关于执行如何异步方法控制,它看起来像一个功能,这将帮助我们实现我们的目标-的也许单子行为的实现。 但是那个单子有什么好处呢? 好吧……您可以在Internet上找到很多有关该monad的文章,因此在这里我将仅介绍基本知识。

也许单子 (Maybe monad)

In short, Maybe monad is a design pattern which allows interruption of a function call chain if some function from the chain cannot produce a valuable result (e.g. parsing error).

简而言之, 也许 monad是一种设计模式,如果该函数调用链中的某个函数无法产生有价值的结果(例如,解析错误),则该模式允许中断该函数调用链。

Historically, imperative programming languages have been solving the problem in two ways:

从历史上看,命令式编程语言一直通过两种方式解决该问题:

  1. Lot of conditional logic

    很多条件逻辑
  2. Exceptions

    例外情况

The both ways have obvious disadvantages, so a third way was invented:

两种方法都有明显的缺点,因此发明了第三种方法:

  1. Create a type which can be in 2 states: "Some Value" and "Nothing" — let's call it "Maybe"

    创建一个可以处于2种状态的类型:“某些值”和“没有”-我们称其为“可能”
  2. Create a function (lets' call it "SelectMany") which retrieves 2 arguments:

    创建一个函数(让我们将其称为“ SelectMany”)以检索2个参数:

    2.1. An object of "Maybe" type

    2.1。 “也许”类型的对象

    2.2. A next function from the call set — the function should also return an object of "Maybe" which will contain a result or "Nothing" if its result cannot be evaluated (e.g. the function parameters are not in correct format)

    2.2。 调用集中的下一个函数-该函数还应该返回一个“ Maybe”对象,如果结果无法评估(例如,函数参数的格式不正确),则该对象将包含结果或“ Nothing”

  3. "SelectMany" function checks if "Maybe" has some value and then calls the next function using the value (extracted from "Maybe") as an argument and then returns its result, otherwise it returns an "Maybe" object in "Nothing" state.

    “ SelectMany”函数检查“ Maybe”是否具有某个值,然后使用该值(从“ Maybe”中提取)作为参数调用下一个函数,然后返回其结果,否则返回“ Nothing”状态的“ Maybe”对象。

In C# it can be implemented like that:

在C#中,可以这样实现:

public struct Maybe
{ public static implicit operator Maybe
(T value) => Value(value); public static Maybe
Value(T value) => new Maybe
(false, value); public static readonly Maybe
Nothing = new Maybe
(true, default); private Maybe(bool isNothing, T value) { this.IsNothing = isNothing; this._value = value; } public readonly bool IsNothing; private readonly T _value; public T GetValue() => this.IsNothing ? throw new Exception("Nothing") : this._value;}public static class MaybeExtensions{ public static Maybe
SelectMany
( this Maybe
source, Func
> func) => source.IsNothing ? Maybe
.Nothing : func(source.GetValue());}

and usage:

和用法:

static void Main(){    for (int i = 0; i < 10; i++)    {        var res = Function1(i).SelectMany(Function2).SelectMany(Function3);        Console.WriteLine(res.IsNothing ? "Nothing" : res.GetValue().ToString());    }    Maybe
Function1(int acc) => acc < 10 ? acc + 1 : Maybe
.Nothing; Maybe
Function2(int acc) => acc < 10 ? acc + 2 : Maybe
.Nothing; Maybe
Function3(int acc) => acc < 10 ? acc + 3 : Maybe
.Nothing;}

为什么选择“ SelectMany”? (Why 'SelectMany'?)

I think that some of you might ask a question: "Why did the author call the function "SelectMany"? It is so strange name". It is, but there is a reason for that. In C# SelectMany is used by the preprocessor to support query notation which simplifies working with call chains (you can find )).

我认为你们中的一些人可能会问一个问题:“为什么作者将函数称为“ SelectMany”?这个名字真奇怪”。 是的,但这是有原因的。 在C#中,预处理器使用SelectMany来支持查询表示法 ,从而简化了调用链的使用(您可以找到 )。

In fact, we can change the call chain as follows:

实际上,我们可以如下更改呼叫链:

var res = Function1(i)    .SelectMany(x2 =>         Function2(x2).SelectMany(x3 =>             Function3(x3.SelectMany
(x4 => x2 + x3 + x4)));

so that we can get access to all intermediate results which is convenient but the code is difficult to read.

这样我们就可以访问所有中间结果,这很方便,但是代码很难阅读。

Here the query notation helps us:

这里的查询符号可以帮助我们:

var res = from x2 in Function1(i)    from x3 in Function2(x2)    from x4 in Function3(x3)    select x2 + x3 + x4;

To make the code compilable we need an enhanced version of "Select Many"

为了使代码可编译,我们需要增强的“选择很多”版本

public static Maybe
SelectMany
( this Maybe
source, Func
> func, Func
joinFunc){ if (source.IsNothing) return Maybe
.Nothing; var res = func(source.GetValue()); return res.IsNothing ? Maybe
.Nothing : joinFunc(source.GetValue(), res.GetValue());}

让我们使用“经典的“也许”实现从文章标题中实现程序 (Let's implement the program from the article header using this 'classical 'Maybe' implementation)

static void Main(){    foreach (var s in new[] {"1,2", "3,7,1", null, "1"})    {        var res = Sum(s);        Console.WriteLine(res.IsNothing ? "Nothing" : res.GetValue().ToString());    }    Console.ReadKey();}static Maybe
Sum(string input) => Split(input).SelectMany(items => Acc(0, 0, items));//Recursion is used to process a list of "Maybes"static Maybe
Acc(int res, int index, IReadOnlyList
array) => index < array.Count ? Add(res, array[index]) .SelectMany(newRes => Acc(newRes, index + 1, array)) : res;static Maybe
Add(int acc, string nextStr) => Parse(nextStr).SelectMany
(nextNum => acc + nextNum);static Maybe
Split(string str){ var parts = str?.Split(',') .Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(); return parts == null || parts.Length < 2 ? Maybe
.Nothing : parts;}static Maybe
Parse(string value) => int.TryParse(value, out var result) ? result : Maybe
.Nothing;

The code does not look great since C# was not designed as a functional language, but in “true” functional languages like Haskell such approach is very common

由于C#不是作为一种功能语言设计的,所以代码看起来不太好,但是在像Haskell这样的“真正”功能语言中,这种方法非常普遍

异步可能 (Async Maybe)

The essence of Maybe monad is to control a function call chain, but it is exactly that "async/await" does. So let's try to combine them together. First, we need to make Maybe type compatible with asynchronous functions and we already know how to do that:

Maybe monad的本质是控制一个函数调用链,但是“ async / await”正是这样。 因此,让我们尝试将它们组合在一起。 首先,我们需要使Maybe类型与异步函数兼容,并且我们已经知道如何做到这一点:

[AsyncMethodBuilder(typeof(MaybeTaskMethodBuilder<>))]public class Maybe
: INotifyCompletion{ ... public Maybe
GetAwaiter() => this; public bool IsCompleted { get; private set; } public void OnCompleted(Action continuation){...} public T GetResult() =>...}

Now let's take a look how the "classical Maybe" can be rewritten as a state machine to be able to find any similarities:

现在,让我们看一下如何将“经典Maybe”重写为状态机,以发现任何相似之处:

static void Main(){    for (int i = 0; i < 10; i++)    {        var stateMachine = new StateMachine();        stateMachine.state = 0;        stateMachine.i = i;        stateMachine.MoveNext();        var res = stateMachine.Result;        Console.WriteLine(res.IsNothing ? "Nothing" : res.GetValue().ToString());    }    Console.ReadKey();}class StateMachine{    public int state = 0;    public int i;    public Maybe
Result; private Maybe
_f1; private Maybe
_f2; private Maybe
_f3; public void MoveNext() { label_begin: switch (this.state) { case 0: this._f1 = Function1(this.i); this.state = Match ? -1 : 1; goto label_begin; case 1: this._f2 = Function2(this._f1.GetValue()); this.state = this._f2.IsNothing ? -1 : 2; goto label_begin; case 2: this._f3 = Function3(this._f2.GetValue()); this.state = this._f3.IsNothing ? -1 : 3; goto label_begin; case 3: this.Result = this._f3.GetValue(); break; case -1: this.Result = Maybe
.Nothing; break; } }}

If we match this state machine with the generated by the C# preprocessor one (see above — 'MyAwaitableMethodStateMachine') we can notice that Maybe state checking can be implemented inside:

如果我们将此状态机与C#预处理程序之一生成的状态机匹配(请参见上面的“ MyAwaitableMethodStateMachine”),我们会注意到也许可以在内部实现状态检查:

this.Builder.AwaitOnCompleted(ref awaiter, ref stateMachine);

where ref awaiter is an object of Maybe type. The only problem here is that we cannot set the machine into the "final" (-1) state. Does that mean that we cannot control the execution flow? Actually, it does not. The thing is that for each asynchronous action C# sets a callback action trough INotifyCompletion interface, so if we want to break an execution flow we can just call the callback action in a case when we cannot continue the flow. Another challenge here is that the generated state machine passes a next action (as a continuation callback) of a current flow, but we need a continuation callback of the initial flow which would allow bypassing the rest of async operations:

其中ref awaiterMaybe类型的对象。 这里唯一的问题是我们无法将机器设置为“最终”(-1)状态。 这是否意味着我们无法控制执行流程? 实际上,事实并非如此。 事实是,对于每个异步动作,C#都会通过INotifyCompletion接口设置一个回调动作,因此,如果我们想中断执行流程,我们可以在无法继续执行流程的情况下调用该回调动作。 这里的另一个挑战是生成的状态机传递当前流的下一个动作(作为继续回调),但是我们需要初始流的继续回调,这将允许绕过其余的异步操作:

So, we need to somehow connect a child async action with its ancestors. We can do that using our "method builder" which has a link to a current async operation — Task. Links to all child async operations will be passed into AwaitOnCompleted(ref awaiter as awaiter, so we just need to check if the parameter is an instance of Maybe and if it is then set the current Maybe as as a parent for the child one:

因此,我们需要以某种方式将子异步操作与其祖先联系起来。 我们可以使用我们的“方法构建器”来做到这一点,该方法链接到当前的异步操作-Task 。 到所有子异步操作的链接都将传递到AwaitOnCompleted(ref awaiter as awaiter ,所以我们只需要检查该参数是否为Maybe的实例,然后将当前Maybe设置为该子代的父代:

[AsyncMethodBuilder(typeof(MaybeTaskMethodBuilder<>))]public class Maybe
: IMaybe, INotifyCompletion{ private IMaybe _parent; void IMaybe.SetParent(IMaybe parent) => this._parent = parent; ...}public class MaybeTaskMethodBuilder
{ ... private void GenericAwaitOnCompleted
( ref TAwaiter awaiter, ref TStateMachine stateMachine) where TAwaiter : INotifyCompletion where TStateMachine : IAsyncStateMachine { if (awaiter is IMaybe maybe) { maybe.SetParent(this.Task); } awaiter.OnCompleted(stateMachine.MoveNext); } ... }

Now all Maybe objects can be joined into a tree and and, as a result, we will get access to a continuation of the root Maybe (Exit method) from any descendant node:

现在,所有Maybe对象都可以连接到树中,因此,我们可以从任何后代节点访问根Maybe的延续( Exit方法):

[AsyncMethodBuilder(typeof(MaybeTaskMethodBuilder<>))]public class Maybe
: IMaybe, INotifyCompletion{ private Action _continuation; private IMaybe _parent; ... public void OnCompleted(Action continuation) { ... this._continuation = continuation; ... } ... void IMaybe.Exit() { this.IsCompleted = true; if (this._parent != null) { this._parent.Exit(); } else { this._continuation(); } } ...}

That Exit method should be called when (during moving over the tree) we found an already resolved Maybe object in Nothing state. Such Maybe objects can be returned by a method like this one:

当(在树上移动期间)我们发现一个已经解析的Maybe对象处于Nothing状态时,应调用Exit方法。 这样的Maybe对象可以通过如下方法返回:

Maybe
Parse(string str) => int.TryParse(str, out var result) ? result : Maybe
.Nothing();

To store a state of resolved Maybe let's introduce a new separate structure:

为了存储已解决状态, 也许我们引入一个新的单独结构:

public struct MaybeResult{    ...    private readonly T _value;    public readonly bool IsNothing;    public T GetValue()         => this.IsNothing ? throw new Exception("Nothing") : this._value;}[AsyncMethodBuilder(typeof(MaybeTaskMethodBuilder<>))]public class Maybe
: IMaybe, INotifyCompletion{ private MaybeResult? _result; ... internal Maybe() { }//Used in async method private Maybe(MaybeResult result) => this._result = result;// "Resolved" instance ...}

When an async state machine calls (through the method builder) OnCompleted method of an already resolved Maybe instance and it is in Nothing state we will be able to break an entire flow:

当异步状态机(通过方法构建器)调用已解析的Maybe实例的OnCompleted方法且它处于Nothing状态时,我们将能够中断整个流程:

public void OnCompleted(Action continuation){    this._continuation = continuation;    if(this._result.HasValue)    {        this.NotifyResult(this._result.Value.IsNothing);    }}internal void SetResult(T result)//Is called by a "method builder" when an async method is completed{    this._result = MaybeResult.Value(result);    this.IsCompleted = true;    this.NotifyResult(this._result.Value.IsNothing);}private void NotifyResult(bool isNothing){    this.IsCompleted = true;    if (isNothing)    {        this._parent.Exit();//Braking an entire flow    }    else    {        this._continuation?.Invoke();    }}

Now the only thing remains — how to get a result of an async Maybe outside of its scope (any async method whose return type is not Maybe). If you try to use just await keyword with a Maybe instance then an exception will be thrown due to this code:

现在唯一剩下的事情是-如何获得超出范围的异步Maybe结果(任何返回类型不是Maybe的异步方法)。 如果您尝试将Maya实例仅使用await关键字,则由于以下代码将引发异常:

[AsyncMethodBuilder(typeof(MaybeTaskMethodBuilder<>))]public class Maybe
: IMaybe, INotifyCompletion{ private MaybeResult? _result; public T GetResult() => this._result.Value.GetValue();}...public struct MaybeResult{ ... public T GetValue() => this.IsNothing ? throw new Exception("Nothing") : this._value;}

To solve the issue we can just add a new Awaiter which would return an entire MaybeResult structure and we will be able to write a code like this:

为了解决这个问题,我们可以添加一个新的Awaiter ,它将返回整个MaybeResult结构,我们将能够编写如下代码:

var res = await GetResult().GetMaybeResult();if(res.IsNothing){    ...}else{    res.GetValue();    ...};

That's all for now. In the code samples I omit some details to focus only on the most important parts. You can find a full version on .

目前为止就这样了。 在代码示例中,我忽略了一些细节,仅将重点放在最重要的部分上。 您可以在上找到完整版本。

However, I would not recommend using this version in any production code since it has a significant issue — when we brake an execution flow by calling a continuation of the root Maybe we will bypass EVERYTHING! including all finally blocks (it is an answer to the question "Are finally blocks always called?"), so all using operators will not work as expected and that might lead to resource leaking. The issue can be solved if instead of calling the initial continuation callback we will throw a special exception which will be handled internally (), but this solution apparently has performance imitations (which might be acceptable in some scenarios). With the current version of the C# compiler I do not see any other solution but that might be changed in future.

但是 ,我不建议在任何生产代码中使用此版本,因为它存在严重的问题-当我们通过调用root的延续来制动执行流时, 也许我们将绕过一切! 包括所有的finally块(这是对“是否总是调用finally块?”这个问题的回答),因此所有使用运算符的操作将无法按预期工作,并且可能导致资源泄漏。 如果我们不是抛出初始的继续回调,而是抛出将在内部处理的特殊异常( ),则可以解决该问题,但是此解决方案显然具有性能仿效(在某些情况下可以接受)。 使用当前版本的C#编译器,我看不到任何其他解决方案,但将来可能会更改。

These limitations do not mean that all the tricks described in this article are completely useless, they can be used to implement other monads which do not require changes in execution flows, for example "Reader". How to implement that "Reader" monad trough async/await I will show .

这些限制并不意味着本文描述的所有技巧都是完全没有用的,它们可用于实现不需要更改执行流程的其他monad,例如“ Reader”。 我将如何通过异步/等待实现“ Reader” monad。

翻译自:

c#async await

转载地址:http://owdwd.baihongyu.com/

你可能感兴趣的文章
css基础
查看>>
如何在tomcat中如何部署java EE项目
查看>>
【Python基础教程第2版】——第二讲:列表和元组
查看>>
小常识
查看>>
使用vscode开发python
查看>>
swift--调用系统单例实现打电话
查看>>
0038-算一算是一年中的第几天
查看>>
51nod 1094 【水题】
查看>>
003.第一个动画:绘制直线
查看>>
ng-深度学习-课程笔记-2: 神经网络中的逻辑回归(Week2)
查看>>
正则表达式的搜索和替换
查看>>
个人项目:WC
查看>>
地鼠的困境SSL1333 最大匹配
查看>>
flume+elasticsearch+kibana遇到的坑
查看>>
【MM系列】在SAP里查看数据的方法
查看>>
C#——winform
查看>>
CSS3 transform制作的漂亮的滚动式导航
查看>>
《小强升职记——时间管理故事书》读书笔记
查看>>
Alpha 冲刺(3/10)
查看>>
Kaldi中的Chain模型
查看>>