gpt4 book ai didi

C# 7 - 为什么我不能从异步方法返回这个等待类型?

转载 作者:行者123 更新时间:2023-11-30 14:23:57 25 4
gpt4 key购买 nike

背景

Try<T>

我正在使用的应用程序使用类型 Try<T>以函数式风格处理错误。 Try<T>实例代表一个值或一个错误,类似于 Nullable<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);
}

async

我正在处理的应用程序也是高度异步的,并且使用标准 async/await成语。代码库专门使用 Task<T> , 没有使用普通的 Taskasync void方法。您通常会在哪里看到 Task , Task<FSharp.Core.Unit>而是使用。

就像你想象的那样,许多异步操作可能会出错,因此类型 Task<Try<T>>经常使用。这工作正常,但会导致很多视觉困惑。由于 C# 7 现在允许 async返回自定义可等待类型的方法,我想使用此功能创建一个有效的类 Task<Try<T>>可以从 async 返回方法。

TryTask<T>

所以我创建了一个自定义的可等待类任务类(它实际上将大部分功能委托(delegate)给了 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);
}

问题

制作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;
}

此方法将无法编译,并出现错误 CS0029:无法隐式转换类型 Try<T2>T2 在符号上 result2在最后一行。

如果我将最后一行更改为 return result2.Value;它会编译,但如果 result2 则无效有错误。

问题

我怎样才能绕过这个错误并让这个类型作为 async 的返回类型工作?方法?在典型 async方法返回 Task<T> ,您可以使用语句 return default(T);编译器将包装 TTask<T>为你。就我而言,我希望它包装一个 Try<T>TryTask<T> , 但编译器期望它应该包装一个 T在某事中。编译器使用什么方法来决定如何进行这种“包装”?

最佳答案

如果我理解正确(没有规范就有点难),根本问题是 async 的类型推断 lambda 表达式,as described here由 tasklike 提案的原作者 Lucian Wischik 撰写。

在您的情况下,这意味着:

void F<T>(Func<TryTask<T>> func) { }

F(async () => Try.Success(42));

lambda 返回 Try<int>并且您希望编译器以某种方式从中找出 lambda 的类型应该是 Func<TryTask<int>> .但是根据上面链接的文档,没有好的方法可以做到这一点。

这不是您的 Bind 的问题,但语言设计者选择让方法和 lambda 的行为保持一致,而不是使方法更强大。

所以,据我所知,你想做的事情是不可能的。您可以考虑通过在 the csharplang repo 上创建问题来与 C# 的设计者分享您的用例。 ,也许有人会弄清楚如何解决这些问题,并在未来的 C# 版本中实现这一点。

关于C# 7 - 为什么我不能从异步方法返回这个等待类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43220955/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com