In C# with nullable types it is possible to implement a 'TryGet' that is smart about null checking, e.g.,
bool TryGetById(int id, [NotNullWhen(returnValue: true)] out MyThing? myThing)
which allows the caller to skip null checking on the out var myThing.
Unfortunately, Async does not allow out parameters, and the pattern of using a Tuple return does not allow for this smart NotNull checking (at least, not so far as I have found). Is there an alternative?
Is there any way to use a 'NotNullWhen' equivalent on an async Tuple return type e.g.,
Task<(bool Ok, [NotNullWhen(returnValue: true)] MyThing? MyThing)> TryGetById(int id)
There isn't an implementation of this for value Tuples (yet). However! From C#9 You could roll-your-own struct (or even better C#10 record struct) with MemberNotNullWhen.
MemberNotNullWhenAttribute Class
Specifies that the method or property will ensure that the listed field and property members have non-null values when returning with the specified return value condition.
Note : You will need to reimplement all the tupley goodness like equality etc.
Worlds most contrived example ensues
#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);
Usage
var result = await TryGetAsync();
if (result.IsGood)
TestMethod(result.Value); // <= no warning
I found another solution, inspired from @TheGeneral's answer that I think results in cleaner operation because you can still use the is keyword to pattern match a tuple.
The generic class
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; } }
}
Example Usage
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
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With