C#7-为什么可以';我不能从异步方法返回这个等待的类型吗? 背景 试试看
我正在使用的应用程序使用类型C#7-为什么可以';我不能从异步方法返回这个等待的类型吗? 背景 试试看,c#,asynchronous,functional-programming,async-await,task,C#,Asynchronous,Functional Programming,Async Await,Task,我正在使用的应用程序使用类型Try以函数式方式处理错误。Try实例表示值或错误,类似于Nullable表示值或null的方式。在函数范围内,可以抛出异常,但将异常“冒泡”到更高级别的组件替换为通过返回值“管道”它们 下面是Try实现的要点。错误类基本上等同于异常 public class Try<T> { private readonly T value; private readonly Error error; public bool HasValue
Try
以函数式方式处理错误。Try
实例表示值或错误,类似于Nullable
表示值或null
的方式。在函数范围内,可以抛出异常,但将异常“冒泡”到更高级别的组件替换为通过返回值“管道”它们
下面是Try
实现的要点。错误
类基本上等同于异常
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);
}
问题
要使TryTask
真正有用,我要做的第一件事是定义函数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;
}
公共异步静态任务绑定(
这个任务来源,
Func绑定)
{
Try result1=等待源;
Try result2=result1.HasValue
?绑定(结果1.值)
:Try.Failure(result1.Error);
返回结果2;
}
此方法将不会编译,错误为CS0029:无法隐式地将最后一行中符号result2
上的typeTry
转换为T2
如果我将最后一行更改为返回result2.Value代码>它将编译,但如果result2
有错误,则编译无效
问题:
如何绕过此错误并使此类型作为async
方法的返回类型工作?在返回任务的典型async
方法中,可以使用语句返回默认值(T)
编译器将把T
包装到任务中。在我的例子中,我希望它将Try
封装在TryTask
中,但编译器希望它将T
封装在某个东西中。编译器使用什么方法来决定如何“包装”?如果我正确理解这一点(没有规范,这有点困难),根本问题是Lucian Wischik提出的async
lambdas的类型推断,他是tasklike提案的原始作者
在您的情况下,这意味着:
void F<T>(Func<TryTask<T>> func) { }
F(async () => Try.Success(42));
void F(Func Func){
F(async()=>Try.Success(42));
lambda返回Try
,您希望编译器从中找出lambda的类型应该是Func
。但根据上面链接的文件,没有好办法做到这一点
这不是您的绑定的问题,但是语言设计者选择让方法和lambda行为一致,而不是使方法更强大
所以,据我所知,你想做的是不可能的。你可以考虑与C的设计者分享你的用例,通过创建一个问题,也许有人会想出如何解决这些问题并在C版本的未来版本中实现这一点。 < P>答案是正确的,但是你可能想考虑那个有A,以及所有扩展方法的Async
变体,以及将a转换为a的ToAsync()
扩展。它还具有和,用于返回Some
、None
、或Fail
// Example of an action that could throw an exception
public Try<int> Foo() => () => 10;
// Synchronous Try
var result = Foo().IfFail(0);
// Synchronous Try
var result = Foo().Match(
Succ: x => x,
Fail: e => 0
);
// Asynchronous Try
var result = await Foo().IfFailAsync(0);
// Asynchronous Try
var result = await Foo().MatchAsync(
Succ: x => x,
Fail: e => 0
);
// Manually convert a Try to a TryAsync. All operations are
// then async by default
TryAsync<int> bar = Foo().ToAsync();
// Asynchronous Try
var result = await bar.IfFail(0);
// Asynchronous Try
var result = await bar.Match(
Succ: x => x,
Fail: e => 0
);
//可能引发异常的操作示例
公共Try Foo()=>()=>10;
//同步尝试
var result=Foo().IfFail(0);
//同步尝试
var result=Foo().Match(
成功:x=>x,
失败:e=>0
);
//异步尝试
var result=await Foo().IfFailAsync(0);
//异步尝试
var result=wait Foo().MatchAsync(
成功:x=>x,
失败:e=>0
);
//手动将Try转换为TryAsync。所有操作都是
//然后默认为异步
TryAsync bar=Foo().ToAsync();
//异步尝试
var结果=等待条IfFail(0);
//异步尝试
var结果=等待条。匹配(
成功:x=>x,
失败:e=>0
);
它有一个函数,如果您正在实现像Try
这样的类型,您可能会发现它很有用。您是否尝试过从Try
中提取T2
的值并返回它?这将编译,但它将忽略result2.HasValue==false
的可能性。必须从函数返回Try
,以便在函数调用链中将错误传输到后面的函数。Try
和TryTask
是否相同?要与正常的async
语法一致,您的方法声明需要返回TryTask
。当然,这与您想要的语法冲突,但基于错误消息,这似乎是编译器想要的。综上所述,我对您重新实现任务的愿望感到困惑。毕竟,任务
已经可以包含结果或错误。TryTask
增加了什么?也许这只是我的想法,但我看不出重点。@PeterDunihoTry
类型背后的想法是在操作的返回类型中包含错误的可能性,而不是“抛出”异常,其中“捕获”是调用代码的可选选项。从某种意义上讲,任务
类似,但它有点不方便,因为它的错误处理设计为适合标准异常抛出。我希望任务始终返回有效的Try
结果(可能包含错误),并且从不引发异常。这统一了任务内部和外部的错误处理,默认系统将其分开。我看到了广义的async
返回类型会使async
lambdas的类型推断更加困难。你是说在异步方法中将return T
标准地“包装”到Task
中是由编译器trea完成的吗
// Example of an action that could throw an exception
public Try<int> Foo() => () => 10;
// Synchronous Try
var result = Foo().IfFail(0);
// Synchronous Try
var result = Foo().Match(
Succ: x => x,
Fail: e => 0
);
// Asynchronous Try
var result = await Foo().IfFailAsync(0);
// Asynchronous Try
var result = await Foo().MatchAsync(
Succ: x => x,
Fail: e => 0
);
// Manually convert a Try to a TryAsync. All operations are
// then async by default
TryAsync<int> bar = Foo().ToAsync();
// Asynchronous Try
var result = await bar.IfFail(0);
// Asynchronous Try
var result = await bar.Match(
Succ: x => x,
Fail: e => 0
);