11

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?

2
  • 1
    Try this approach without the use of Span link Commented Jul 27, 2019 at 4:37
  • sharplab.io/… Commented Jul 27, 2019 at 4:44

3 Answers 3

23

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

Alternate way using .Net 8 and SearchValues

I've wrapped this in an async method to match the signature from the user's given question. In performant real-world code it would not be needed.

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

public class Program
{
    private static System.Buffers.SearchValues<char> base64SearchValues = System.Buffers.SearchValues.Create(@"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
    public async static 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
        Console.WriteLine(await IsBase64String(null)); // False
        Console.WriteLine(await IsBase64String("")); // False
    }

    public async static Task<bool> IsBase64String(string base64)
    {
        // Note the use of `AsSpan()`
        return await Task.FromResult(
            base64?.Length > 0 &&
            base64?.Length % 4 == 0 &&
            base64.AsSpan().IndexOfAnyExcept(base64SearchValues) < 0
        );
    }
}

the code on dotnetfiddle.net

Sign up to request clarification or add additional context in comments.

5 Comments

it must be an example, but marking a method async and awaiting a Task.FromResult is useless.
@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?
@jeroen-van-langen oh gotcha and yes but, I was trying to match the original post's method signature which is async.
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.
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.
10

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);
}

5 Comments

removing is not a solution, take a look at answer below stackoverflow post, it show the uses of span and memory with async
@Saurabh: That answer shows that Memory<T> can be used with async, which is correct. Span<T> cannot be used with async, so the op must either remove the Span<T> or remove the async. Since there's no await, the async can be removed without changing semantics.
Your answer is not solution var message = await rsp.Content.ReadAsByteArrayAsync(); var doc = new System.Text.Json.Utf8JsonReader(message);
@nim: Again, it is not possible to have Span<T> in an async method. Either the async must be removed (this answer), or the Span<T> must be removed (see the top-voted answer).
3

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);
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.