dotnet / docs

This repository contains .NET Documentation.
https://learn.microsoft.com/dotnet
Creative Commons Attribution 4.0 International
4.26k stars 5.89k forks source link

C# Guide: Tutorial for writing generalized async return types #1756

Closed BillWagner closed 3 years ago

BillWagner commented 7 years ago

C# 7 relaxes the rules on the return types for async methods. These new rules are demonstrated by the ValueTask type in the latest framework. That handles some of the most likely scenarios.

However, we should write a tutorial that shows readers how to create their own types that satisfy the pattern for a return type from async methods.

ac10n commented 7 years ago

I'll share my scenario, it may be useful for the tutorial:

Like most other developers, I have my own way of making use of technology. In using webapi, I create a generic result class that will tell the client if the execution was successful, contains the result if so, and information about the problem if not. Something like this:

public class Result<T>
{
    public bool Succeeded { get; set; }
    public T Data {get; set;}
    public ErrorCategory ErrorCategory {get; set;}
    public string ErrorMessage { get; set; }
    public InputValidaitionFailureInfo InputValidationFailureInfo {get; set;}
}

This is a very simplified version. The actual one inherites from a non-generic one which contains lots of factory methods to create the Result<T>. Also, it implements IResult<out T> so that I don't need to cast my List<Person> to IEnumerable<Person> before creating a Result<T>.

Now the problem: the methods are async, to use all the benefits of asynchronous programming, so the return type of the method becomes:

public async Task<IResult<IEnumerable<Person>>>>

and this is going to repeat in every damn api I'm implementing. It's against my general rule: less boilerplate and more logic. It also causes actual damage: someone forgets to include IResult in the return type, then returns T instead of 'Result`, it slips through review, makes its way to production and now it lives in the api signature, it might be a little difficult to change.

The solution is to write a class ResultTask<T> which returns an IResult<T> after completion and a ListResultTask<T> which gives us an IResult<IEnumerable<T>>. Please forget the bad naming, as the classes are not created yet, This will clean up the code.

I can also contribute to the tutoral, if someone can point me in the right direction.

Thanks

BillWagner commented 7 years ago

@AlirezaHaghshenas That's a really useful scenario. We'll triage this issue and we'd appreciate any help putting it together. We'll make sure to tag you as we get it put together.

ac10n commented 7 years ago

@BillWagner As I read the sources for ValueTask<>, I noticed that my scenario can be easily handled by creating a class that wraps Task<Result<T>> and another one that wraps Task<Result<IEnumerable<T>>.

Do you have a scenario in mind that requires creating a class from scratch, totally separate from Task<T>?

BillWagner commented 3 years ago

This issue has been closed as part of the issue backlog grooming process outlined in #22351.

That automated process may have closed some issues that should be addressed. If you think this is one of them, reopen it with a comment explaining why. Tag the @dotnet/docs team for visibility.