Async Interfaces feature request please. #1177
Replies: 14 comments
-
So all return types should automatically replaced with their task (default task implementation) equivalent? Or should that be done by hand, and verified by the compiler? I see the use of it, great idea. I just don't remember ever wanting to have this feature. |
Beta Was this translation helpful? Give feedback.
-
So you're asking for the compiler to emit and use an |
Beta Was this translation helpful? Give feedback.
-
@jnm2 I am not attached to the semantics here, hopefully someone with a much deeper understanding of the language could suggest a much better way of declaring this than I have suggested. But it would probably be in a different namespace? @Mafii yes if they are just automatically replaced with their task equivalent that would probably cover all the use-cases I have in mind. But if there could be some more fine grain control over this so much the better. I suppose what I really want to do is to dynamically generate a proxy class based on an interface, all our request code is pretty boiler plate and could be auto generated. The best I can do at the moment would be something that implements
But if I wanted all my calls to be async, I need someway of turning |
Beta Was this translation helpful? Give feedback.
-
The syntax suggested I think would be very confusing. The class doesn't implement I think that something like this could fall under the mythical source generator umbrella. You could decorate the class with an attribute that specifies that it "pseudo-implements" the interface in an async manner and also specify what kind of strategy to use when generating the async methods. The question there would be what would the source generator actually generate given that it would fall on your class to actually provide the implementation of those methods. Only thing I could think of would be for the generator to emit a new async-flavor of the interface and then have your class implement it: You would write: [AsyncImplementation(typeof(IFoo), WithCancellationToken = true, WithAsyncSuffix = true)]
public partial class Foo
{
} And the generator could emit: public partial class Foo : Foo.IAsyncFoo
{
public interface IAsyncFoo
{
Task<int> GetAsync(int i, CancellationToken cancellationToken);
}
} And then you'd have to amend your code to implement [AsyncImplementation(typeof(IFoo), WithCancellationToken = true, WithAsyncSuffix = true)]
public partial class Foo
{
public async Task<int> GetAsync(int i, CancellationToken cancellationToken)
{
// do stuff here
}
} That would allow the generator to provide options to allow different strategies of code generation. I wonder if maybe the source generator proposal could consider additional syntax that would allow generators to emit that a partial class must implement certain members. (#753 would seemingly handle that nicely.) |
Beta Was this translation helpful? Give feedback.
-
What is the point? Couldn't you just use |
Beta Was this translation helpful? Give feedback.
-
or you can move async part into separate method. for example some thing like |
Beta Was this translation helpful? Give feedback.
-
Plus, if you don't want to implement it in every class, default interface implementations might help, interface IFoo
{
// implement each method in terms of the other
// then you only need to implement either of those
int Get(int i) { ... }
Task<int> GetAsync(int i) { ... }
} Which is a terrible idea. |
Beta Was this translation helpful? Give feedback.
-
Isn't this what libraries like protobuffs are for? You supply an interface stating your protocol, and you get the appropriate client and server code gened for you do all this work. |
Beta Was this translation helpful? Give feedback.
-
FWIW, TypeScript has something like this: // Same property names, but make the value a promise instead of a concrete one
type Deferred<T> = {
[P in keyof T]: Promise<T[P]>;
}; If this were done in a similar vein to TypeScript, I might get behind it because that could open the door to other TypeScript equivalents like |
Beta Was this translation helpful? Give feedback.
-
Codegen FTW :) I actually have done something similar: https://github.com/Pzixel/RemoteClient.Roslyn Works just fine. I agree that it would be nice to have it in the language itself, but as you said it's not really convinient for statically typed one. |
Beta Was this translation helpful? Give feedback.
-
@Pzixel Rather than a "traditional" .NET code generation approach, I'd much rather see this be possible a la the proposed C++ Using the IFoo example from OP, here's some pseudo-C# gratuitously re-using the public interface IFooAsync {
constexpr {
ConvertToAsync<IFoo>();
}
} Which would generate: public interface IFooAsync {
Task<int> GetAsync(int i);
} Of course in this trivial example it is less code to just write it out yourself, but perhaps this could be achieved via some decorator attribute, i.e. The public static constexpr ConvertToAsync<T>() where T : class {
foreach (var method in typeof(T).GetMethods()) {
// i.e. check to make sure method isn't already async, handle void -> Task, etc. etc.
var returnType = typeof(Task<>).MakeGenericType(method.ReturnType);
var name = $"{method.Name}Async";
var parameters = method.GetParameters(); // transform somehow?
-> {
$returnType$ $name$($parameters$);
}
}
} However, I don't know how this would handle scenarios like circular references. But it's fun to think about. |
Beta Was this translation helpful? Give feedback.
-
Sounds like a very similar approach to source generators, except with a lot of additional syntax changes directly to the language. I don't see how that would work in a compiled form, e.g. a helper assembly you can acquire via NuGet? |
Beta Was this translation helpful? Give feedback.
-
@HaloFour It is very similar to T4, but can be used inline in your source and runs through the same compiler as the rest of your code so you get built-in reflection and type safety at compile time. Think of it as a better alternative to macros. I would imagine you could produce Here's an alternative, more type-safe version of that method with less syntax changes needed, just the public static constexpr IEnumerable<InterfaceMethod> ConvertToAsync<T>() where T : class {
foreach (var method in typeof(T).GetMethods()) {
// i.e. check to make sure method isn't already async, handle void -> Task, etc. etc.
var returnType = typeof(Task<>).MakeGenericType(method.ReturnType);
var name = $"{method.Name}Async";
var parameters = method.GetParameters();
yield return new InterfaceMethod(returnType, name, parameters);
}
} |
Beta Was this translation helpful? Give feedback.
-
By "source generators" I'm not referring to the existing T4 methods for generating source within a project, I am referring specifically to the proposal here to integrate source generation into analyzers so that they are a part of the compilation pipeline. Those "generators" would be referenced into a project like analyzers are today, via NuGet, and would automatically integrate into the editor experience. I included some source above as to what that might look like to a consumer when implementing a class. But it could also be applied to interfaces directly, e.g.: [ConvertToAsync(typeof(IFoo))]
public partial interface IFooAsync { } And a generator could identify all marked interfaces and emit source for the members: // generated
public partial interface IFooAsync
{
Task<int> GetAsync(int i, CancellationToken cancellationToken);
} That would be completely seamless to the developer (they could view the generated source, but they don't have to worry about maintaining it, even if As a championed proposal this approach seems much more likely to find adoption. The issue with it seems to be the amount of load it puts on the editor to provide a good experience given that virtually any change to the source could have a sweeping impact. Your approach is interesting but it involves a significant amount of changes to the language and would likely suffer from the same problem that generators face in that the editor experience would be very difficult to get right. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi there,
I want to be able to implement the same shared interface on both the client and server but on the client I want to implement the interface with an async signature and on the server I want to implement the sync signature.
Would it be possible to do something like the following:
I realize that this probably isn't as easy as I am making it sound but I am really just looking for a way to define a contract which the compiler could check has been implemented in either a sync or an async fashion.
Beta Was this translation helpful? Give feedback.
All reactions