Jam*_*aix 6 c# asynchronous functional-programming task async-await
Try<T>我正在使用的应用程序使用类型Try<T>来处理函数风格的错误。实例Try<T>表示值或错误,类似于 aNullable<T>表示值或 的方式null。在函数范围内,可能会引发异常,但将异常“冒泡”到更高级别的组件会被通过返回值“管道传输”所取代。
这是实施的要点Try<T>。类Error基本上相当于Exception.
public class Try<T> {
private readonly T value;
private readonly Error error;
public bool HasValue { get; }
public T Value {
get {
if (!HasValue) throw new InvalidOperationException();
return value;
}
}
public Error Error {
get {
if (HasValue) throw new InvalidOperationException();
return error;
}
}
internal Try(Error error) {
this.error = error;
}
internal Try(T value) {
this.value = value;
HasValue = true;
}
}
public static class Try {
public static Try<T> Success<T>(T value) => new Try<T>(value);
public static Try<T> Failure<T>(Error error) => new Try<T>(error);
}
Run Code Online (Sandbox Code Playgroud)
async我正在开发的应用程序也是高度异步的,并使用标准async/await习惯用法。代码库专门使用Task<T>,不使用普通的旧方法Task或async void方法。您通常会看到的地方使用Task,来代替。Task<FSharp.Core.Unit>
正如您可能想象的那样,许多异步操作可能会出错,因此该类型Task<Try<T>>被大量使用。这工作得很好,但会导致很多视觉混乱。由于 C# 7 现在允许async方法返回自定义可等待类型,因此我想使用此功能来创建一个Task<Try<T>>可以从方法返回的有效类async。
TryTask<T>因此,我创建了一个自定义的可等待任务类(它实际上将大部分功能委托给一个Task<Try<T>>字段)和一个随附的AsyncMethodBuilder类。
[AsyncMethodBuilder(typeof(TryTaskBuilder<>))]
public class TryTask<T>
{
private readonly Task<Try<T>> _InnerTask;
public TryTask(Func<Try<T>> function)
{
if (function == null) throw new ArgumentNullException(nameof(function));
_InnerTask = new Task<Try<T>>(function);
}
internal TryTask(Task<Try<T>> task)
{
_InnerTask = task;
}
public void Start() => _InnerTask.Start();
public TaskStatus Status => _InnerTask.Status;
public Try<T> Result => _InnerTask.Result;
public TaskAwaiter<Try<T>> GetAwaiter() => _InnerTask.GetAwaiter();
public void Wait() => _InnerTask.Wait();
}
public static class TryTask
{
public static TryTask<T> Run<T>(Func<Try<T>> function)
{
var t = new TryTask<T>(function);
t.Start();
return t;
}
public static TryTask<T> FromValue<T>(T value) => new TryTask<T>(Task.FromResult(Try.Success(value)));
public static TryTask<T> FromError<T>(Error error) => new TryTask<T>(Task.FromResult(Try.Failure<T>(error)));
public static TryTask<T> FromResult<T>(Try<T> result) => new TryTask<T>(Task.FromResult(result));
public static TryTask<T> FromTask<T>(Task<Try<T>> task) => new TryTask<T>(task);
}
public class TryTaskBuilder<T>
{
private AsyncTaskMethodBuilder<Try<T>> _InnerBuilder;
public TryTaskBuilder()
{
_InnerBuilder = new AsyncTaskMethodBuilder<Try<T>>();
}
public static TryTaskBuilder<T> Create() =>
new TryTaskBuilder<T>();
public TryTask<T> Task =>
default(TryTask<T>);
public void Start<TStateMachine>(ref TStateMachine stateMachine)
where TStateMachine : IAsyncStateMachine =>
_InnerBuilder.Start(ref stateMachine);
public void SetStateMachine(IAsyncStateMachine stateMachine) =>
_InnerBuilder.SetStateMachine(stateMachine);
public void SetResult(Try<T> result) =>
_InnerBuilder.SetResult(result);
public void SetException(Exception exception) =>
_InnerBuilder.SetResult(exception.AsError<T>());
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : INotifyCompletion
where TStateMachine : IAsyncStateMachine =>
_InnerBuilder.AwaitOnCompleted(ref awaiter, ref stateMachine);
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
where TAwaiter : ICriticalNotifyCompletion
where TStateMachine : IAsyncStateMachine =>
_InnerBuilder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
}
Run Code Online (Sandbox Code Playgroud)
为了使其TryTask<T>真正有用,我要做的第一件事是定义函数和高阶函数let,它们将“展开”值并用它们执行操作。这是一个例子:bind,map
public async static TryTask<T2> Bind<T1, T2>(
this TryTask<T1> source,
Func<T1, Try<T2>> binding)
{
Try<T1> result1 = await source;
Try<T2> result2 = result1.HasValue
? binding(result1.Value)
: Try.Failure<T2>(result1.Error);
return result2;
}
Run Code Online (Sandbox Code Playgroud)
此方法将无法编译,并出现错误CS0029:无法将类型隐式转换Try<T2>为T2result2最后一行中的符号。
如果我将最后一行更改为它将编译,但如果出现错误return result2.Value;,则该行无效。result2
如何解决此错误并让此类型作为async方法的返回类型?在返回 的典型async方法中Task<T>,您可以使用该语句return default(T);,编译器会将其包装T在 a 中Task<T>。就我而言,我希望它将 a 包装Try<T>在 a 中TryTask<T>,但编译器希望它将 a 包装T在某物中。编译器使用什么方法来决定如何进行这种“包装”?
如果我理解正确的话(如果没有规范,这有点困难),根本问题是asynclambda 的类型推断,正如任务提案的原始作者 Lucian Wischik 所描述的那样。
就你而言,这意味着:
void F<T>(Func<TryTask<T>> func) { }
F(async () => Try.Success(42));
Run Code Online (Sandbox Code Playgroud)
lambda 返回Try<int>,您希望编译器以某种方式从中找出 lambda 的类型应该是Func<TryTask<int>>。但根据上面链接的文档,没有好的方法可以做到这一点。
这对您来说不是问题Bind,但语言设计者选择让方法和 lambda 表现一致,而不是使方法更强大。
所以,据我所知,你想做的事情是不可能的。您可以考虑通过在csharplang repo中创建问题来与 C# 设计者共享您的用例,也许有人会弄清楚如何解决这些问题并在 C# 的未来版本中实现这一点。