Search code examples
c#async-awaitnull

C# member to check property for null and set it with async function if needed?


This is NOT a failure of anything. I'm just trying to determine how to write c# more elegantly.

In a C# project I find I am doing an awful lot of

if (null != classX.user || await classX.getUserAsync())
{
     // getUserAsync() either sets classX.user appropriately and returns true, or else returns false.
     // Calls to classX members and other functions that are only OK when classX.user is valid follow

and I'd like to bundle this into a member function like

private async Task<bool> IsUserSet()
{
     if (null != user)
         return await Task.FromResult<bool>(user);
     
     return await getUserAsync();
}

because (IMHO) replacing the if statement with if (IsUserSet()) is more readable.

However, I'm wondering if the performance hit (there is one with inserting another Task isn't there?) is worth it, or if there is a way to do this without inserting another task. (Or maybe I just need some education on how to write cleaner ASP.NET code!)

*** FINAL Edit after learning a lot from all the comments:

  • I should consider a ValueTask for any intermediate Task functions (or when appropriate), it can reduce overhead (thanks Charlieface).
  • By using ValueTask in SetUserAsync (was GetUserAsync), I can incorporate the "null" check inside the SetUserAsync function instead of creating an intermediate function (Akash, Pedro).
  • Generally, I shouldn't need to return Task.FromResult<> (I don't know where I picked up that habit) it adds unnecessary overhead (phuzi).

Solution

  • I think you were nearly there with

    private async Task<bool> IsUserSet()
    {
         if (null != user)
             return await Task.FromResult<bool>(user);
         
         return await getUserAsync();
    }
    

    but return await Task.FromResult<bool>(user); would need to be return true;

    You could simplify this and do something similar to your original code.

    private async Task<bool> IsUserSet()
    {
         return user is not null || await getUserAsync();
    }
    

    Sidenote on await Task.FromResult()

    When you do await Task.FromResult<bool>(user) you have redundant code.

    Task.FromResult<bool>(user) wraps user in a completed task and prefixing it with await automatically unwraps it, so await Task.FromResult<bool>(user) can be reduced to user as you weren't actually returning Task<bool> but bool all along and marking a method as async automatically wraps the returned value in a task`