C# 异步TryParse(…)模式

C# 异步TryParse(…)模式,c#,async-await,C#,Async Await,在.NET BCL中有许多常见的bool-TryXXX(out-T-result)方法,最流行的可能是int.TryParse(…) 我想实现一个异步TryXXX()方法。显然,我不能使用out参数 这有一个既定的模式吗 更重要的是,我需要下载并解析一个文件。该文件可能不存在 这就是我到目前为止的想法: public async Task<DownloadResult> TryDownloadAndParse(string fileUri) { try {

在.NET BCL中有许多常见的
bool-TryXXX(out-T-result)
方法,最流行的可能是
int.TryParse(…)

我想实现一个异步
TryXXX()
方法。显然,我不能使用
out
参数

这有一个既定的模式吗

更重要的是,我需要下载并解析一个文件。该文件可能不存在

这就是我到目前为止的想法:

public async Task<DownloadResult> TryDownloadAndParse(string fileUri)
{
    try
    {
        result = await DownloadAndParse(fileUri); //defined elsewhere
        return new DownloadResult {IsFound = true, Value = result}
    }
    catch (DownloadNotFoundException ex)
    {
        return new DownloadResult {IsFound = false, Value = null}
    }
    //let any other exception pass
}

public struct DownloadResult
{
    public bool IsFound { get; set; }

    public ParsedFile Value { get; set; }
}
public异步任务trydownloaddparse(字符串fileUri)
{
尝试
{
结果=等待下载解析(fileUri);//在别处定义
返回新的下载结果{IsFound=true,Value=result}
}
捕获(下载NotFoundException ex)
{
返回新的下载结果{IsFound=false,Value=null}
}
//让任何其他例外通过
}
公共结构下载结果
{
找到公共布尔值{get;set;}
公共解析文件值{get;set;}
}

我提出了以下定义。
defaultValue
参数主要用于重载
TryGet
方法,因为泛型约束不是方法签名的一部分,这使得在决定调用哪个方法时方法是唯一的(例如,返回类型也不是签名的一部分)

public async Task TryGet(Func Func,T defaultValue=null),其中T:class
{
尝试
{
return wait func();
}
捕获(异常)
{
返回默认值;
}
捕获(格式化异常)
{
返回默认值;
}
捕获(溢出例外)
{
返回默认值;
}
}
公共异步任务TryGet(Func Func,可为null的defaultValue=null),其中T:struct
{
尝试
{
return wait func();
}
捕获(异常)
{
返回默认值;
}
捕获(格式化异常)
{
返回默认值;
}
捕获(溢出例外)
{
返回默认值;
}
}
您应该检查异常处理,本例处理常见的解析异常。对其他异常做出反应可能更有意义,例如
invalidooperationexception
NotSupportedException
,它们可能是框架本身最常用的异常类型(不一定是最常抛出的异常)

另一种方法是重新抛出关键异常,例如
ThreadAbortException
,并使用一个简单的catch all子句返回默认值。然而,这将隐藏所有不被视为关键的异常,无论其严重程度如何

因此,由于抛出异常是一项昂贵的操作,因此通常根据
TryParse
定义
Parse
。因此,您的
TryGet
应该有一个合同,例如处理
OperationCanceledException
,其中包括
TaskCanceledException
和其他内容


最后,您应该按照异步后缀约定将其命名为
TryGetAsync

可能的决定之一是一个包含0或1个元素的
ParsedFile
数组

public async Task<ParsedFile[]> TryDownloadAndParse(string fileUri)
{
    try
    {
        return new[] { await DownloadAndParse(fileUri) };
    }
    catch (DownloadNotFoundException ex)
    {
        return new ParsedFile[0];
    }
}
如果要调用
void
方法,可以使用
?。
运算符:

var parsedFiles = await TryDownloadAndParse(url);
parsedFiles.SingleOrDefault()?.DoVeryImportantWorkWithoutResult();
更新


在Azure中,您可以使用。

在实现此语法之前,您似乎注定只能返回自己的类型。至少使用Tuple(或者更好的自定义类型,具有合理的属性名称,如Success和Result),而不是KeyValuePair.Thx作为链接,很好的建议!元组比KVP有什么好处?我总是需要准确地返回两项,真/假结果和实际负载。我有点讨厌
Tuple
.Item1
.Item2
…对Tuple的内容完全不透明。)的属性名
KVP
并没有好多少(在我的场景中),但至少
.Value
是有意义的。区别只是语义。KeyValuePair是key+value。在你的情况下,没有什么是真正关键的。元组只是两个(或多个)任意值-您的情况。如果不喜欢名称,请使用具有适当名称的bool和t属性创建自己的TryAsyncResult类。对于所有的Try操作,您只需要一个这样的类(因为泛型)。@CristiDiaconescu-,值得一读。性能问题。但从实际情况看,没有区别。围绕您最初的问题,您能解释一下想要异步解析的场景吗?我看不出哪里需要它异步?@LordJam我的方法进入云存储,下载一个特定格式的文件并解析它,返回一个强类型表示。
Try
部分是因为文件可能不存在,这是一个“正常”(即非异常)情况-它不保证异常。ConditionalValue听起来很有趣。该页面提到“可靠的集合API”,但没有链接,而且缺乏详细信息……PS发现“可靠的集合”的更好链接似乎是Azure service Fabric的服务产品。
. . .

var parsedFiles = await TryDownloadAndParse(url);
if (parsedFiles.Any())
{
    var parsedFile = parsedFiles.Single();
    // more processing
}

. . .
var parsedFiles = await TryDownloadAndParse(url);
parsedFiles.SingleOrDefault()?.DoVeryImportantWorkWithoutResult();