Click here to Skip to main content
15,881,701 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
I could use some help getting the WriteMultipleCoilsAsync task to run. I'm getting the error message Cannot assign void to an implicitly-typed variable on the Task.Run statement. I need to capture the response message from WriteMultipleCoilsAsync.

Here the code:

C#
private async void btnWriteMultipleCoils_Click(object? sender, EventArgs e)
    {
        string discretes = "true,false,true";
        bool[] diarray = Array.ConvertAll(discretes.Split(','), bool.Parse);
        var response = await Task.Run(() => master.WriteMultipleCoilsAsync(0, diarray));
        await Task.CompletedTask;
    }

Here is the WriteMultipleCoilsAsync task:
C#
public Task WriteMultipleCoilsAsync(ushort startAddress, bool[] data)
    {
        return WriteMultipleCoilsAsync(0, startAddress, data);
    }

Here is the WriteMultipleCoilsAsync task:
C#
public Task WriteMultipleCoilsAsync(byte slaveAddress, ushort startAddress, bool[] data)
    {
        ValidateData("data", data, 1968);
        WriteMultipleCoilsRequest request = new WriteMultipleCoilsRequest(slaveAddress, startAddress, new DiscreteCollection(data));
        return PerformWriteRequestAsync<WriteMultipleCoilsResponse>(request);
    }


What I have tried:

Numerous attempts, but nothing working so far.
Posted
Updated 16-Nov-22 22:50pm
v2

There is a lot wrong here.
1. The method that you want to await is not marked as async
2. The asynchronous call to PerformWriteRequestAsync in method WriteMultipleCoilsAsync is not awaited.

You should have:
C#
private async void btnWriteMultipleCoils_Click(object? sender, EventArgs e)
{
	string discretes = "true,false,true";
	bool[] diarray = Array.ConvertAll(discretes.Split(','), bool.Parse);
	.. bad! following two lines will lock the UI negating the benefits of asychronous programming
	// var response = await Task.Run(() => master.WriteMultipleCoilsAsync(0, diarray));
	// await Task.CompletedTask;
	
	// asynchronous call + force off UI thread
	var response = await master.WriteMultipleCoilsAsync(0, diarray).ConfigureAwait(false);
}

public async Task<WriteMultipleCoilsResponse> WriteMultipleCoilsAsync(ushort startAddress, bool[] data)
{
	return await WriteMultipleCoilsAsync(0, startAddress, data).ConfigureAwait(false);
}

public async Task<WriteMultipleCoilsResponse> WriteMultipleCoilsAsync(byte slaveAddress, ushort startAddress, bool[] data)
{
	ValidateData("data", data, 1968);
	WriteMultipleCoilsRequest request = new WriteMultipleCoilsRequest(slaveAddress, startAddress, new DiscreteCollection(data));
	return await PerformWriteRequestAsync<WriteMultipleCoilsResponse>(request).ConfigureAwait(false);
}

WARNING
async void is dangerous. It is a call and forget asynchronous call. If you want the UI to wait until the call is completed, then you need a isBusy boolean variable or a callback method, depending on your requirements, to track and manage the call state.

You can read more here: Asynchronous programming in C# | Microsoft Learn[^] or watch this video: Async Best Practices for C# and Visual Basic - YouTube[^] - the video goes into the issues and how to fix them. Mads is one of the C# language designers, so he is the best to learn from.

Hope this helps!
 
Share this answer
 
v2
Comments
Richard Deeming 17-Nov-22 4:06am    
"The method that you want to await is not marked as async"
That's not a problem; you can await anything that returns a Task, a ValueTask, or anything that exposes an appropriate GetAwaiter method / extension method. You only need async on a method if you want to await within that method.
Graeme_Grant 17-Nov-22 4:10am    
Yes, it is, but they need to understand the basics first before making decisions like these.
Richard Deeming 17-Nov-22 4:07am    
"The asynchronous call to PerformWriteRequestAsync in method WriteMultipleCoilsAsync is not awaited"
Again, if the method is not marked as async, then it's perfectly acceptable to return a Task from another method without awaiting it. :)
Graeme_Grant 17-Nov-22 4:20am    
I am fully aware of this but you're asking me to teach more advanced concepts to someone who has no grasp of the basics.

Teach then to walk first, not run then walk.
Richard Deeming 17-Nov-22 4:22am    
I agree with the sentiment. But telling someone that their valid code is wrong, and needs to be rewritten in a more complicated way, doesn't seem to follow from that. :)
Graeme (Solution 1) is correct that you should avoid async void[^] wherever possible. Unfortunately, that doesn't address the actual problem.

Your two WriteMultipleCoilsAsync methods return Task, not Task<T>. That means there is no result returned from the Task. You can await it to know when it has completed, but there is nothing to store in your response variable.

Assuming your PerformWriteRequestAsync<WriteMultipleCoilsResponse> method returns a Task<WriteMultipleCoilsResponse>, you simply need to update both overloads of the WriteMultipleCoilsAsync method to return the same:
C#
public Task<WriteMultipleCoilsResponse> WriteMultipleCoilsAsync(ushort startAddress, bool[] data)
{
    return WriteMultipleCoilsAsync(0, startAddress, data);
}

public Task<WriteMultipleCoilsResponse> WriteMultipleCoilsAsync(byte slaveAddress, ushort startAddress, bool[] data)
{
    ValidateData("data", data, 1968);
    WriteMultipleCoilsRequest request = new WriteMultipleCoilsRequest(slaveAddress, startAddress, new DiscreteCollection(data));
    return PerformWriteRequestAsync<WriteMultipleCoilsResponse>(request);
}
To avoid the async void, you will want to move the code that calls this into a separate Task-returning method:
C#
private async Task WriteMultipleCoilsAsync()
{
    string discretes = "true,false,true";
    bool[] diarray = Array.ConvertAll(discretes.Split(','), bool.Parse);
    
    // Or, more simply:
    // bool[] diarray = { true, false, true };

    var response = await master.WriteMultipleCoilsAsync(0, diarray);
    // Do something with the response here...
}
You can then "fire and forget" this task-returning async method from the event handler, using a "discard" to avoid the compiler warning:
C#
private void btnWriteMultipleCoils_Click(object? sender, EventArgs e)
{
    _ = WriteMultipleCoilsAsync();
}
 
Share this answer
 
Comments
Graeme_Grant 17-Nov-22 5:07am    
This is still Fire and Forget just pushed to another method. The OP still needs to consider how to manage the result of the call or the time that the async call executes. This I mention in my original answer and is a point that you missed.
Richard Deeming 17-Nov-22 5:13am    
Yes, it's still "fire and forget"; but it doesn't have the same associated problems as an async void method. And it's one approach suggested in David Fowler's async guidance[^], so it's a valid approach.

The alternative would be to use something like Marc Gravell's "fire and forget" code[^]; but that adds even more complexity when we're trying to stick to the basics. :)
Graeme_Grant 17-Nov-22 5:16am    
Again, you're pushing more advanced techniques onto a novice. Running before walking. You totally missed the more important part of the section of my answer clearly labeled WARNING.
Richard Deeming 17-Nov-22 5:19am    
No, I'm not pushing the more advanced technique. I'm just suggesting the code that needs to be async should be in a task-returning method. The discard is just there to suppress the compiler warning. :)
Graeme_Grant 17-Nov-22 5:19am    
Again, This is still Fire and Forget. Keep it KISS...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900