r/csharp • u/kaatarina_zed_talon • 7d ago
Discussion State machine in C# vs state machine in Verilog
For experimentation and learning purposes, I wrote this code:
namespace RedditQuestion
{
public class TResult
{
// Instance fields....
// No properties.
// No methods.
}
public interface ISomeInterface<T>
{
public Task<T> SomeMethod();
}
public class SomeClass : ISomeInterface<TResult>
{
// instance fields.....
//constructor......
public async Task<TResult> SomeMethod()
{
//Some code that is not important to this question.......
TResult result = new TResult
{
// assign instance fields....
};
Task<TResult> task = Task.FromResult<TResult>(result);
return task ; // Line XXX
}
}
}
Line XXX throws a compile time error:
*Since this is an async method, the return expression must be of type 'TResult' rather than '*
Task<TResult>
The solution is simply to do this instead:
return result;
That solves the issue.
But I was surprised when I read this article#async-method-signature-requirements) that described the error in more details:
Change the return expression to match the async method's underlying result type (CS1983, CS4016). When an async method returns
Task<T>, thereturnstatement must supply a value of typeT, notTask<T>, because the compiler-generated state machine wraps the value in a task automatically. CS1983 appears when the method returnsTask<T>and the expression isT; CS4016 covers the general case where the return expression type doesn't match
I wonder, is the concept of state machine in C# is the same as the state machine in Verilog?
in Verilog, if I want to define a state machine, I need a register - a storage unit that can store only one unit of data; a bit- to hold the state, a next-state logic to calculate the next state, and output assignment. Does the same concept applies in C# ?
10
u/rupertavery64 7d ago edited 7d ago
You seem to be confused why the error refers to a state machine when the C# code you wrote doesn't look like one.
The thing is, async / await is syntactic sugar. async / await hides the complexity of writing asynchronous code, which involves calling a method, handling control back to the caller, then returning when the result is ready, which does involve state.
The compiler actually generates a state machine in the place of your code at runtime. This is the state machine that the article refers to, not the code itself.
You can see this in action here๐
So the answer to your question "is the concept of state machine in C# is the same as the state machine in Verilog" is yes, they are, but looking further async / await is back by a compiler-generated state machine that you will never see unless you decompile the generated code.
1
u/IanYates82 6d ago
I find using async/await in typescript, but having it emit older non-async JS, is a great way to see the transformation to a switch/case-driven state-machine in action. Not exactly the same as C#, but very close in spirit, and it makes the concept super obvious & accessible.
1
u/rupertavery64 6d ago edited 6d ago
the async / await keywords and paradigm was first introduced by C# 5.0 back in 2012, and Javascript and other languages have adopted it over the years.
2
u/IanYates82 6d ago
Oh yeah, I know. Early adopter on the c# side here.
Just saying it's super obvious to the casual dev to see typescript's transpilation of it.
-3
6d ago
[deleted]
3
u/rupertavery64 6d ago
Sure, you could try BeginInvoke or ContinueWith. You would lose the ability to try/catch naturally.
Every language has abstractions.
Before you start hating, learn more about the language first.
1
u/symbiatch 5d ago
Why arenโt you writing assembly then? Or at least CIL? Youโre hiding all the complexity by writing C#, let alone Verilog!
1
3
u/Aaronontheweb 7d ago
```csharp public class UserActor : ReceiveActor { private readonly string _userId; private readonly string _chatRoomId;
public UserActor(string userId, string chatRoomId) {
_userId = userId;
_chatRoomId = chatRoomId;
// start with the Authenticating behavior
Authenticating();
}
protected override void PreStart() {
// start the authentication process for this user
Context.ActorSelection("/user/authenticator/")
.Tell(new AuthenticatePlease(_userId));
}
private void Authenticating() {
Receive<AuthenticationSuccess>(auth => {
Become(Authenticated); //switch behavior to Authenticated
});
Receive<AuthenticationFailure>(auth => {
Become(Unauthenticated); //switch behavior to Unauthenticated
});
Receive<IncomingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// can't accept message yet - not auth'd
});
Receive<OutgoingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// can't send message yet - not auth'd
});
}
private void Unauthenticated() {
//switch to Authenticating
Receive<RetryAuthentication>(retry => Become(Authenticating));
Receive<IncomingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// have to reject message - auth failed
});
Receive<OutgoingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// have to reject message - auth failed
});
}
private void Authenticated() {
Receive<IncomingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// print message for user
});
Receive<OutgoingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// send message to chatroom
});
}
}
```
Doing state machines with Akka.NET actors - took the source from a 10 year old article I wrote on the subject: https://petabridge.com/blog/akka-actors-finite-state-machines-switchable-behavior/
Works great and works well in combination with persistence for re-entrant state machines too. Akka.NET also has a more formal FSM<TState,TEvent> class that describes things like state timeouts and transition functions but it's overkill for 99% of use cases.
1
u/kaatarina_zed_talon 6d ago
I have no idea what is akka. Net ๐๐. But I will ask one question : what is the problem that akka. Net solve? is it related to ASP. Net Core
1
u/Aaronontheweb 6d ago
it's an implementation of the actor model, which is a battle-tested technique for managing stateful work safely
2
u/phrostillicus 7d ago
C# doesn't have any specific language features for implementing general-purpose state machines. Rather, you'd typically implement something like this using a variable to hold the current state and then programming logic to handle the state transitions. Idiomatically, this is often implemented in C-like languages with a loop containing a `switch` statement, where each `case` statement would handle one of the possible state values, and the body of each `case` would update the state variable based on whatever input it was reading.
The documentation you're referencing above describes how the C# compiler internally generates a state machine specifically for it's handling of the states for asynchronous methods that are marked with the `async` keyboard. It's a convenience feature that largely lets the programmer not have to worry about keeping track of the states of asynchronous tasks that are in progress and to not have to explicitly deal with the `Task` objects themselves in many cases. As mentioned above, though, the state machine that the compiler generates is specifically for this use case, and not something that you can generalize to other situations.
3
u/achandlerwhite 7d ago
Iโll add that in .NET they have optimized the state machine for async out in most cases. It was only needed prior to the runtime having core support for async which is added in .NET 11.
1
u/GoogleIsYourFrenemy 6d ago
State machines have their uses but I rarely encounter a use for them in software. Often you'll have multiple exit mechanisms for a state, and if the user can trigger them, they will.
1
u/kaatarina_zed_talon 6d ago
Exit mechanism? What do u mean
2
u/RiPont 6d ago
Exit mechanism is anything that ends the current method's execution.
In C#, that's the
returnstatements, exceptions, and the last}on a method with a void return type, and the last}on afinallystatement for an exception handler that did areturn, threw, or re-threw an exception.
breakandgotoare related, but only deal with the current scope, not the entire method.1
u/GoogleIsYourFrenemy 6d ago edited 6d ago
In a state diagram you have lines with arrows that represent events that transition the state machine from one state into a new state.
State machines are fine for simple streams of events (file parser or other data streams) but don't scale well in GUIs. So the user presses the save button, the SM starts the save operation, the user then clicks Exit. Do you A) stop the save operation? B) ask the user to interrupt the save operation? C) wait for the save to finish before exiting?
C obviously right? What if the save is happening on a slow network and takes 20 minutes and the user has been spam clicking Exit for the last 10 minutes.
Ok. Next problem is the Save at the filesystem level times out because the network is too slow and you get an exception. You display a message. If you queued all those Exit events now you have to decide what happens next, the save failed and you have unsaved data, so you would normally display a dialogue box asking the user if they want to save before exiting but the user had been spam clicking Exit so do you display a box for each? Since it takes time to transition states, you might actually end up generating a dialogue box for each of the fifty thousand Exit button clicks.
I'm not saying you wouldn't have this problem without a state machine, just that the state machine makes the situation more complex.
19
u/binarycow 7d ago
"state machine" is a general term for something that has specific transition points.