在具有可空类型的 C# 中,可以实现智能地进行空检查的“TryGet”,例如,
bool TryGetById(int id, [NotNullWhen(returnValue: true)] out MyThing? myThing)
这允许调用者跳过对 out var myThing 的 null 检查。
不幸的是,异步不允许输出参数,并且使用元组返回的模式不允许这种智能的 NotNull 检查(至少,据我所知)。有替代方案吗?
有没有办法在异步元组返回类型上使用“NotNullWhen”等效项,例如,
Task<(bool Ok, [NotNullWhen(returnValue: true)] MyThing? MyThing)> TryGetById(int id)
还没有针对 value Tuples 的实现。然而!从C#9开始,您可以使用struct
struct
(或者更好的C#10记录
MemberNotNullWhen
)。
指定方法或属性将确保列出的 返回时,字段和属性成员具有非空值 指定的返回值条件。
注意:您将需要重新实现所有tupley的优点,例如平等等。
世界上最做作的例子随之而来
#nullable enable
public readonly struct Test
{
[MemberNotNullWhen(returnValue: true, member: nameof(Value))]
public bool IsGood => Value != null;
public string? Value { get; init; }
}
public static Task<Test> TryGetAsync()
=> Task.FromResult(new Test {Value = "bob"});
public static void TestMethod(string bob)
=> Console.WriteLine(bob);
用法
var result = await TryGetAsync();
if (result.IsGood)
TestMethod(result.Value); // <= no warning
MyThing
的实施,您可以这样做
public class MyThing
{
public static readonly MyThing Empty = new();
// all other properties etc
}
然后让你的方法签名永远不会返回 null Mything
public async Task<(bool Ok, MyThing MyThing)> TryGetById(int id)
{
var something = await FindSomething(id);
return (something == null)
? (false, MyThing.Empty);
: new (true, something);
}
var result = await TryGetById(420);
if(result.Ok) // whatever
泛型类
public class AsyncParseResult<T> : Tuple<bool, T?>
{
public AsyncParseResult(bool item1, T? item2) : base(item1, item2) { }
[MemberNotNullWhen(returnValue: true, member: nameof(Item2))]
public new bool Item1 { get { return base.Item1; } }
public new T? Item2 { get { return base.Item2; } }
}
使用示例
public class Thing
{
}
public static class Test
{
public static async Task<AsyncParseResult<Thing>> TryParseThingAsync(string value)
{
if(value == "thing")//you would replace this with your real parsing logic that uses async
{
return new AsyncParseResult<Thing>(true, new Thing());
}
else
{
return new AsyncParseResult<Thing>(false, null);
}
}
public static async Task ExampleUsage()
{
if(await TryParseThingAsync("thing") is (true, var thing))
{
//thing will not be null here
}
}
}