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
上的type
Try
转换为
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
增加了什么?也许这只是我的想法,但我看不出重点。@PeterDuniho
Try
类型背后的想法是在操作的返回类型中包含错误的可能性,而不是“抛出”异常,其中“捕获”是调用代码的可选选项。从某种意义上讲,
任务
类似,但它有点不方便,因为它的错误处理设计为适合标准异常抛出。我希望任务始终返回有效的
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
);