7

I am trying to determine valid base64 string in async method using codes below:

public static async Task<bool> IsBase64String(string base64)
{
    Span<byte> buffer = new Span<byte>(new byte[base64.Length]);
    return await Task.FromResult(Convert.TryFromBase64String(base64, buffer, out int bytesParsed));
}

But Span can not be declared in async method, any body please help?

Johnny
  • 8,939
  • 2
  • 28
  • 33
Donald
  • 551
  • 2
  • 6
  • 22
  • 1
    Try this approach without the use of Span [link](https://stackoverflow.com/questions/6309379/how-to-check-for-a-valid-base64-encoded-string#21242217) – Giddy Naya Jul 27 '19 at 04:37
  • https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEA3GUCWAZgJ4A+AAgEwCMAsAFDkAMABOdQKwDcD5AzG2pI2lFgGEANgEMAzjJYBvBixVsB7YeQAcbJAB5gECBIB8LAJIyAQrJioAyhgIA7AOYAKdq2C3UASkVlVWCAWRgAW2hiA2IMGDNgAFdCQjwWAF4WZxgAdxYwyKho4Fj492y8kriAbR8ZOxQAOgAZGDcMAAsAXT8eemDg8gB2cQhnXCgMRoAVIoAxKAhwm3qHJ3w3dzqGtBYklLxG+wAHKWddiESMFg3rqpgZAAUpKHqAE16glQBfBm+gA= – user4003407 Jul 27 '19 at 04:44

3 Answers3

15

While Stephen's answer is correct... if you need to use a Span<T> in an async method and you can use C# 7 or above, then you can take advantage of a language feature called local functions to solve for the Parameters or locals of type 'T' cannot be declared in async methods or lambda expressions error. Below is an example of how you could do that... and once again this is meant to solve for the reason given.

using System;
using System.Text;
using System.Threading.Tasks;

public class Program
{
    public static async Task Main(string[] args)
    {
        var base64Str = Convert.ToBase64String(Encoding.UTF8.GetBytes("this is a Base64 str"));
        var notBase64Str = "this is not a Base64 str";
        Console.WriteLine(await IsBase64String(base64Str)); // True
        Console.WriteLine(await IsBase64String(notBase64Str)); // False
    }

    public static async Task<bool> IsBase64String(string base64)
    {
        return await Task.FromResult(CheckIfIsBase64String());
        // Note the use of a local non-async function so you can use `Span<T>`
        bool CheckIfIsBase64String()
        {
            // the use of stackalloc avoids array heap allocation
            Span<byte> buffer = stackalloc byte[base64.Length];
            return Convert.TryFromBase64String(base64, buffer, out int bytesParsed);
        }
    }
}

the code on dotnetfiddle.net

Al Dass
  • 831
  • 15
  • 23
  • 4
    it must be an example, but marking a method async and awaiting a Task.FromResult is useless. – Jeroen van Langen Feb 07 '21 at 12:23
  • @jeroen-van-langen given I have `Parameters or locals of type 'T'` this method was given to get around that issue (as my description states). More importantly, how would you do it differently? – Al Dass Feb 07 '21 at 14:35
  • @jeroen-van-langen oh gotcha and yes but, I was trying to match the original post's method signature which is async. – Al Dass Feb 07 '21 at 15:08
  • 4
    You could remove the `async` and the `await`. just return that task directly. By marking the method as `async` it will create another task which is return. Meaning, you're making this method async so it will be split-up into a statemachine and within this statemachine you are awaiting the Task.FromResult. – Jeroen van Langen Feb 07 '21 at 15:09
  • 1
    This feels so weird that you have to make a separate function and isolate the synchronous part of your code from the async part. Seems like something the compiler would be able to do potentially itself, but maybe it's not possible. The MSDN docs don't explain this usage pattern well at all. – MgSam Jul 25 '23 at 13:00
7

I am trying to determine valid base64 string in async method

Why? There's nothing asynchronous about that method. The proper way to solve this problem is to remove the async keyword:

public static bool IsBase64String(string base64)
{
  Span<byte> buffer = new Span<byte>(new byte[base64.Length]);
  return Convert.TryFromBase64String(base64, buffer, out int bytesParsed);
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • removing is not a solution, take a look at answer below stackoverflow post, it show the uses of span and memory with async – Saurabh Nov 25 '22 at 11:38
  • 1
    https://stackoverflow.com/questions/47321691/what-is-the-difference-between-spant-and-memoryt-in-c-sharp-7-2 – Saurabh Nov 25 '22 at 11:38
  • @Saurabh: That answer shows that `Memory` can be used with `async`, which is correct. `Span` cannot be used with `async`, so the op must either remove the `Span` or remove the `async`. Since there's no `await`, the `async` can be removed without changing semantics. – Stephen Cleary Nov 25 '22 at 13:29
  • Your answer is not solution var message = await rsp.Content.ReadAsByteArrayAsync(); var doc = new System.Text.Json.Utf8JsonReader(message); – nim Dec 12 '22 at 17:04
  • @nim: Again, it is not possible to have `Span` in an `async` method. Either the `async` must be removed (this answer), or the `Span` must be removed (see the top-voted answer). – Stephen Cleary Dec 12 '22 at 19:34
2

If you observe System.IO.Stream methods, for example, you will see that, synchronous methods Stream.Read() and Stream.Write() accept Span<byte> and asynchronous methods Stream.ReadAsync() and Stream.WriteAsync() accept Memory<byte>. So if you are using true asynchronous methods, you shouldn't need to create Span<>.

In your case you don't need to wrap the result in Task. The operation will complete synchronously and will be written in the Task. So when you wrap it in Task and await it, you are literally saying "Wrap this result in task, and then give me the result of the task.".

But if you really want to return Task from your method, you can remove the async and await keywords. The method will still be awaitable from outside. (Again: In this case there is no need to wrap the result in Task)

// Removed async await
public static Task<bool> IsBase64StringAsync(string base64)
{
    Span<byte> buffer = new Span<byte>(new byte[base64.Length]);
    return Task.FromResult(Convert.TryFromBase64String(base64, buffer, out int bytesParsed));
}

// Synchronous method
public static bool IsBase64String(string base64)
{
    Span<byte> buffer = new Span<byte>(new byte[base64.Length]);
    return Convert.TryFromBase64String(base64, buffer, out int bytesParsed);
}
Mitko Petrov
  • 311
  • 1
  • 4
  • 12