Reputation: 513
After C# 5 introduced the async
and await
model for asynchronous programming, the C# community arrived at a naming convention to add an "Async" suffix to methods returning an awaitable type, like this:
interface Foo
{
Task BarAsync();
}
Many static code analyzers (both Roslyn-based and non-Roslyn-based) have since been written to depend on this naming convention when detecting code smell around asynchronous programming.
Now that C# 8 has introduced the concept of asynchronous enumerables, which themselves are not awaitable but can be used in conjunction with await foreach
, there seems to be two options for naming methods returning IAsyncEnumerable
:
interface Foo
{
// No "Async" suffix, indicating that the return value is not awaitable.
IAsyncEnumerable<T> Bar<T>();
}
or
interface Foo
{
// With "Async" suffix, indicating that the overall logic is asynchronous.
IAsyncEnumerable<T> BarAsync<T>();
}
Has there been a definitive naming convention guideline (from the C# language team, the .NET Foundation, or other authorities) regarding the options above, like how the C# 5 naming convention was unambiguously standardized and not left to opinion-based judgement of programmers?
Upvotes: 23
Views: 2735
Reputation: 31
The answer, per the .Net team, seems to be that methods returning IAsyncEnumerable<> ought to end in “Async”.
Stephen Toub, one of the .Net runtime superstars, stated so for public APIs for C# streams, on this GitHub issue: https://github.com/dotnet/runtime/issues/27547#issuecomment-478384285
Yes [the method names will end in “Async”]. The most common consumption will be via await foreach, and it serves as a similar visual indicator of where asynchrony is involved, allows for differentiation from sync enumerables, etc.
Upvotes: 3
Reputation: 131324
There's no better guideline than what the .NET teams already do :
IAsyncEnumerable<T>
IAsyncEnumerable
by calling AsAsyncEnumerable()IAsyncEnumerable
sSystem.Linq.Async
retain their names. There's no SelectAsync
or SelectAsAsyncEnumerable
, just Select
. In all cases, it's clear what the result of the method is. In all cases, the results of the method need to be awaited with await foreach
before they can be used.
So the real guideline remains the same - ensure the name makes the behavior clear:
AsAsyncEnumerable()
or ToAsyncEnumerable()
, there's no need to add any suffix. Async
suffix so developers know they need to await foreach
the result.The code analyzers and generators don't really care about the names of methods, they detect smells by inspecting the code itself. A code analyzer will tell you that you forgot to await a Task or await foreach
an IAsyncEnumerable
no matter how you call the methods and the variables. A generator can simply use reflection to check for IAsyncEnumerable
and emit await foreach
It's the style analyzers that check names. Their job is to ensure the code uses a consistent style so developers can understand the code. The style analyzer will tell you that a method doesn't follow the style you chose. That style may be the team's or a commonly accepted style guide.
And of course, everyone knows the common prefix for private instance fields is _
:)
Upvotes: 15
Reputation: 7526
Async suffix and even keyword async
is just boilerplate, it is meaningless other than some backward compatability arguments. C# has all information to distinct those functions just like it distincts yield
in methods which return IEnumerable
, as you know you don't need to add anything like enumerable
or some other keyword on such methods.
You need to add Async suffix just because C# will complain about overloads, so essentially those two are identical and they do same thing by all rules of polymorphism (if we are not taking human factor of intentially corrupting behavior into account):
public interface IMyContract
{
Task<int> Add(int a, int b);
int Add(int a, int b);
}
But you can't write it like this because compilator will complain. But, hey! It will swallow this one monstrosity:
public interface IMyContract : IEnumerable<int>, IEnumerable<long>
{
}
and even this:
public interface IMyContractAsync
{
Task<int> Add(int a, int b);
}
public interface IMyContract : IMyContractAsync
{
int Add(int a, int b);
}
So this is it. You add Async just to stop compiler to complain. Not to clarify things. Not to make them better. If there is no complain - no reason to add it, so in your case I will avoid this, unless it becomes Task<IAsyncEnumerable>
.
PS: Just for better of understanding of entire picture here - if sometime in future someone add in C# quantum-computer method extensions (fantazy, huh) which will operate in different paradigm, we probably will need another suffix like Quant or something, because C# will complain otherwise. It will be exact same method but it will do it's work another way. Just like polymorphism do. Just like interfaces do.
Upvotes: 0