在 C# 中,如何使用 `TryAsync` 的实例?
In C#, how can I consume an instance of `TryAsync`?
我有以下方法:
private async Task<(bool, string)> Relay(
WorkflowTask workflowTask,
MontageUploadConfig montageData,
File sourceFile,
CancellationToken cancellationToken
)
{
try
{
byte[] fileContent = await _httpClient.GetByteArrayAsync(sourceFile.Url, cancellationToken);
await _attachmentController.TryUploadAttachment(montageData.EventId, fileContent, sourceFile.Name);
return (true, null);
}
catch (Exception exception)
{
_logger.LogError(exception, $"File cannot be uploaded: {sourceFile.Name}", workflowTask);
return (false, exception.ToString());
}
}
我想重构它以使用 LanguageExt.Core
中的 TryAsync
(或其他一些功能性 Try
类型)。
我已经设法将上述方法重构为:
private TryAsync<bool> Relay(
MontageUploadConfig montageData,
File sourceFile,
CancellationToken cancellationToken
) => new(async () =>
{
byte[] fileContent = await _httpClient.GetByteArrayAsync(sourceFile.Url, cancellationToken);
return await _attachmentController.TryUploadAttachment(montageData.EventId, fileContent, sourceFile.Name);
});
这可以编译,但我无法使用结果,无论是执行接下来的操作还是记录异常。
如何检查结果并对返回值或任何异常进行处理?
TryAsync
的declaration是:
public delegate Task<Result<A>> TryAsync<A>();
所以你可以这样做:
var result = await Relay(...)()
并获得 Result<bool>
。消费Result
的方式有很多种。例如
result.IfFail(exception => { ... });
result.IfSucc(result => { ... });
当前在 Relay
中构造 TryAsync
的方法不正确。例如假设你有这个:
static async Task<string> MightThrow() {
await Task.Delay(0);
throw new Exception("test");
}
然后您按照现在的方式从中构建 TryAsync
:
static TryAsync<string> WrongTryAsync() {
return new (async () => {
return await MightThrow();
});
}
现在如果你尝试食用这个会发生什么?
var tryAsync = WrongTryAsync();
var result = await tryAsync();
它会在 await
上抛出异常,这不是您在这种情况下所期望的,您期望的结果是失败状态。因此,虽然您 return 匹配 TryAsync
签名 - 这还不够。
库有一种以预期方式构造TryAsync
的方法:
using static LanguageExt.Prelude; // not necessary but convenient
...
static TryAsync<string> CorrectTryAsync() {
return TryAsync(MightThrow);
// use static call if you didn't add using static above
// return LanguageExt.Prelude.TryAsync(MightThrow);
}
现在上面使用它的代码不会抛出但 return 预期的失败结果:
var tryAsync = CorrectTryAsync();
var result = await tryAsync();
// result.IsFaulted == true;
因此,在构建正确的 TryAsync
之后 - 您可以通过执行委托(await ...()
)从中获取结果,然后使用结果,或者使用 TryAsync
本身,因为库直接为它提供了相当多的扩展方法。
编辑:不必立即解包 TryAsync
的结果,您可以对 TryAsync
本身执行各种操作,因为它是一个 monad。由于您有 scala 背景,我们可以查看 this example 并做类似的事情。
using System;
using System.Threading.Tasks;
using static LanguageExt.Prelude;
namespace ConsoleApp4 {
class Program {
static async Task Main(string[] args) {
await Test();
Console.ReadKey();
}
static async Task Test() {
// we have two async methods which return ints, and we want to divide them, but any of them might throw
// we want to do that in a functional fashion
// I use varibles for clarity, you can combine all this into one function call chain.
var dividend = TryAsync(GetDividend);
var divisor = TryAsync(GetDivisor);
// now apply the division function to the (wrapped) values of our TryAsync instances
var result = apply((a, b) => a / (float)b, dividend, divisor);
// decide what to do on success or on failure and execute
await result
.Match(
r => Console.WriteLine($"Success, result is: {r}"),
ex => Console.WriteLine($"Failed to compute, exception was: {ex}")
);
}
static async Task<int> GetDividend() {
await Task.Delay(0);
// or might throw
return 10;
}
static async Task<int> GetDivisor() {
await Task.Delay(0);
return 5;
}
}
}
我有以下方法:
private async Task<(bool, string)> Relay(
WorkflowTask workflowTask,
MontageUploadConfig montageData,
File sourceFile,
CancellationToken cancellationToken
)
{
try
{
byte[] fileContent = await _httpClient.GetByteArrayAsync(sourceFile.Url, cancellationToken);
await _attachmentController.TryUploadAttachment(montageData.EventId, fileContent, sourceFile.Name);
return (true, null);
}
catch (Exception exception)
{
_logger.LogError(exception, $"File cannot be uploaded: {sourceFile.Name}", workflowTask);
return (false, exception.ToString());
}
}
我想重构它以使用 LanguageExt.Core
中的 TryAsync
(或其他一些功能性 Try
类型)。
我已经设法将上述方法重构为:
private TryAsync<bool> Relay(
MontageUploadConfig montageData,
File sourceFile,
CancellationToken cancellationToken
) => new(async () =>
{
byte[] fileContent = await _httpClient.GetByteArrayAsync(sourceFile.Url, cancellationToken);
return await _attachmentController.TryUploadAttachment(montageData.EventId, fileContent, sourceFile.Name);
});
这可以编译,但我无法使用结果,无论是执行接下来的操作还是记录异常。
如何检查结果并对返回值或任何异常进行处理?
TryAsync
的declaration是:
public delegate Task<Result<A>> TryAsync<A>();
所以你可以这样做:
var result = await Relay(...)()
并获得 Result<bool>
。消费Result
的方式有很多种。例如
result.IfFail(exception => { ... });
result.IfSucc(result => { ... });
当前在 Relay
中构造 TryAsync
的方法不正确。例如假设你有这个:
static async Task<string> MightThrow() {
await Task.Delay(0);
throw new Exception("test");
}
然后您按照现在的方式从中构建 TryAsync
:
static TryAsync<string> WrongTryAsync() {
return new (async () => {
return await MightThrow();
});
}
现在如果你尝试食用这个会发生什么?
var tryAsync = WrongTryAsync();
var result = await tryAsync();
它会在 await
上抛出异常,这不是您在这种情况下所期望的,您期望的结果是失败状态。因此,虽然您 return 匹配 TryAsync
签名 - 这还不够。
库有一种以预期方式构造TryAsync
的方法:
using static LanguageExt.Prelude; // not necessary but convenient
...
static TryAsync<string> CorrectTryAsync() {
return TryAsync(MightThrow);
// use static call if you didn't add using static above
// return LanguageExt.Prelude.TryAsync(MightThrow);
}
现在上面使用它的代码不会抛出但 return 预期的失败结果:
var tryAsync = CorrectTryAsync();
var result = await tryAsync();
// result.IsFaulted == true;
因此,在构建正确的 TryAsync
之后 - 您可以通过执行委托(await ...()
)从中获取结果,然后使用结果,或者使用 TryAsync
本身,因为库直接为它提供了相当多的扩展方法。
编辑:不必立即解包 TryAsync
的结果,您可以对 TryAsync
本身执行各种操作,因为它是一个 monad。由于您有 scala 背景,我们可以查看 this example 并做类似的事情。
using System;
using System.Threading.Tasks;
using static LanguageExt.Prelude;
namespace ConsoleApp4 {
class Program {
static async Task Main(string[] args) {
await Test();
Console.ReadKey();
}
static async Task Test() {
// we have two async methods which return ints, and we want to divide them, but any of them might throw
// we want to do that in a functional fashion
// I use varibles for clarity, you can combine all this into one function call chain.
var dividend = TryAsync(GetDividend);
var divisor = TryAsync(GetDivisor);
// now apply the division function to the (wrapped) values of our TryAsync instances
var result = apply((a, b) => a / (float)b, dividend, divisor);
// decide what to do on success or on failure and execute
await result
.Match(
r => Console.WriteLine($"Success, result is: {r}"),
ex => Console.WriteLine($"Failed to compute, exception was: {ex}")
);
}
static async Task<int> GetDividend() {
await Task.Delay(0);
// or might throw
return 10;
}
static async Task<int> GetDivisor() {
await Task.Delay(0);
return 5;
}
}
}