Discussion: async constructors #419
Replies: 98 comments 115 replies
-
Hmmm... You can technically do that with a void-returning async method already. I would not advise doing that or implementing the above, however; that sounds like an antipattern. "After the constructor returns, you should get a fully initialized object. Not an object that will be actually properly initialized at some undefined point in the future. That is, if you're lucky and the async initialization doesn't fail." Do we really want to deal with half-baked objects, which initialization can take an arbitrarily long time, occur at an unpredictable time, or fails in unpredictable ways, throwing all sorts of exception, including I/O? Until the object is constructed, there would be no real way to monitor the state or cancel or provide progress reports. You noticed that an async constructor could be called on one thread, then finish on another. Sounds like a debugging nightmare. In addition, most developers assume that constructors do not throw, except developer-misuse exceptions (ArgumentException). This feature would introduce the same difficulties in object initialization than we currently have with object destruction (unpredictable GC, etc...). This said, there clearly is a need for async initialization. I would suggest to rethink the object lifecycle as whole and create a standard "init" pattern - implemented perhaps via a new standard interface, an init analog to IDisposable - instead of trying to cram everything in the constructor:
|
Beta Was this translation helpful? Give feedback.
-
Isn't it unexpected that Should it also work with tasklikes e.g. |
Beta Was this translation helpful? Give feedback.
-
My objections, TL;DR:
|
Beta Was this translation helpful? Give feedback.
-
It being a constructor syntactically would allow us to manage inheritance and constructor chaining which is not that straightforward if written manually, it requires rather complicated logic depending on various aspects.
The generated private constructor does not do the heavy lifting. It's just the input syntax that is useful in the cases mentioned above.
No, I'm using
With the transformation I've suggested we first calculate awaited values and pass them to a real constructor to be assigned to readonly fields, so no clr change is required, albeit preferable for an efficient implementation. |
Beta Was this translation helpful? Give feedback.
-
This was mentioned in the original issue as I was proposing a different syntax to instantiate such types. I've removed it based on @CyrusNajmabadi suggestion. You could use the "Async" suffix naming convention just like methods for that to indicate that it has an async constructor and can be awaited, for example. |
Beta Was this translation helpful? Give feedback.
-
Which would render it impossible to reference |
Beta Was this translation helpful? Give feedback.
-
It's possible to call |
Beta Was this translation helpful? Give feedback.
-
When? Not if Factory methods aren't pretending to be constructors. They don't come with any expectation that you can refactor an existing constructor by sprinkling a few |
Beta Was this translation helpful? Give feedback.
-
I'd prefer Notice that this does not break backward compatibility. The only break in back-compat would come when you change the definition of |
Beta Was this translation helpful? Give feedback.
-
We extract out all sealed class C
{
private readonly T1 _f1;
public async C() => _f1 = this.F(await GetData());
// ->
private C(T1 f1) => _f1 = this.F(f1);
public static async Task<C> CreateAsync() => new C(await GetData());
} The |
Beta Was this translation helpful? Give feedback.
-
About this, in Rust for example, a constructor is just a convenient method to create the object. Every type gets a "default constructor" that requires every field to be initialized, it does not allow you to run any actual logic in it, so you may not call any instance method before the final object is fully initialized. It's restricted on the language level while in C# it's still possible probably for historical reasons. So referencing |
Beta Was this translation helpful? Give feedback.
-
Indeed, which is exactly why you can't touch
It's bad practice to call instance methods? Set instance fields or properties? Not in C# it's not. You can't even chain to synchronous base constructors in the order described by the source. The amount of code-rewriting/reordering that would be required to facilitate supporting |
Beta Was this translation helpful? Give feedback.
-
That's why I said "passing", in case of a field or auto property you are not passing All in all, I admit that if we use constructors merely for initialization and factor out async constructor initializers, async constructors do not provide much value compared to factory methods. The question is that how much those patterns are common and what are the alternatives. |
Beta Was this translation helpful? Give feedback.
-
I feel like a static factory is sufficient to handle these scenarios. I also think they are easier to read. var x = await SomeType.CreateAsync(a, b); easier to understand than var x = await new SomeType(a, b); Additionally, factories have several advantages
Another, reason I don't think this is a great idea is that the return type of a constructor is not something that one generally considers. Constructors just create instances of their class. But with |
Beta Was this translation helpful? Give feedback.
-
TLDR; In favor of this because:
|
Beta Was this translation helpful? Give feedback.
-
As it appears there is no technical reason why constructors couldn't be asynchronous, the discussion seems to boil down to two issues.
I think the answer to this is yes, as the following are true Developers are calling Wait() in constructors (obviously bad)
Is there anything wrong with...
This isn't legal syntax now, so it wouldn't break anything and it's very concise. I know nothing about compiler implementation, but it seems like the compiler should be smart enough to emit the above code as an awaitable Task. It already looks like C# 9 is tackling issues related to class initialization, it would be a shame if async constructors didn't make the cut. |
Beta Was this translation helpful? Give feedback.
-
Regardless of anything else, we're not going to get to async construction in C# 9. With the exception of a couple of very small features, everything that is going to make it has been shipped with 16.7 preview 3. |
Beta Was this translation helpful? Give feedback.
-
Bump - any update on this? C# 10.0 maybe? |
Beta Was this translation helpful? Give feedback.
-
In favor! It will make codebases easier to read when we standardize such an important pattern. |
Beta Was this translation helpful? Give feedback.
-
I am in favor of this because of of modifying the SqlConnection inside the constructor of the EF DbContext
|
Beta Was this translation helpful? Give feedback.
-
Does anyone know which is the current 'best practice' way of solving / implementing a workaround for this?
I know i've resorted to some of these myself in the past... (i think i've done 1,2,4,5) |
Beta Was this translation helpful? Give feedback.
-
I'd also like to see this because of one other issue that the factory pattern runs into. Since the object is not being initialized in the constructor, readonly properties don't work. The C# 9 |
Beta Was this translation helpful? Give feedback.
-
Interesting to see how this thread has gotten a lot of traction but when I suggested it a couple of years ago, it got one simple, "no, the point of a constructor is to run before logic, closed" -answer. Anyway, I really like the suggestion about adding the async keyword on the class to communicate that this has asynchronous constructors, but what if I want a synchronous constructor as well? Will i be forced to always do a I bring this up because I would want to apply this when I inherit from an abstract class, but that class might not be an "async class" |
Beta Was this translation helpful? Give feedback.
-
I understand both the arguments that Another point: @aluanhaddad mentioned that factory methods (in addition to already being available) are better because they can be referenced by delegates. Is it possible to (either using a language trick or in the runtime) to create constructor delegates? (This is outside of the |
Beta Was this translation helpful? Give feedback.
-
I read your response and thought about how to wrap it all up in syntactic sugar. The conclusion I reached is that this would produce complex implicit behaviour and steep learning curves. However, you way requires that the change in behaviour should be _requested_. I could go with that.
Also, there is the fact that readiness is a property of the object unrelated to construction. A database connection object may be ready, have a network glitch, go unready, then recover and be ready again. You can solve that with exactly the same readiness pattern, and your notation could be applied.
I wonder why these guys are having such difficulties... dotnet/aspnetcore#24142<dotnet/aspnetcore#24142>
If they used the readiness pattern and people let go of the sometimes unsatisfiable expectation that everything will be ready quickly, the problem would stop being hard. It wouldn't satisfy your aesthetic, but that's another matter.
Sent from Mail<https://go.microsoft.com/fwlink/?LinkId=550986> for Windows
…________________________________
From: Jack Bond ***@***.***>
Sent: Saturday, February 12, 2022 10:02:20 PM
To: dotnet/csharplang ***@***.***>
Cc: Peter Wone ***@***.***>; Mention ***@***.***>
Subject: Re: [dotnet/csharplang] Discussion: async constructors (Discussion #419)
"Your team should understand really clearly that creating an object and getting it ready to use are two separate things."
Even though I completely disagree with that statement, I'm going to go ahead and accept it for sake of argument. If being ready is a real world distinction, than THE ENTIRE ECOSYSTEM should recognize and promote it to an officially recognized and supported construct. And let's roll with your Ready property. For example, when I've been passed an object, I don't want to have to check if its ready every time I use it, so let's add a c# operator, how about !!! which tells the compiler "Check if this thing is ready, and if it isn't, then make it ready" For example,
public class Foo
{
public Foo(Bar !!!bar)
{
}
}
is obviously cleaner than
public class Foo
{
public Foo(Bar bar)
{
bar.Ready().Wait();
}
}
We now live in an injection world, when I'm handed an object, I expect it to be ready without having to check something. I wonder why these guys are having such difficulties... dotnet/aspnetcore#24142<dotnet/aspnetcore#24142>
—
Reply to this email directly, view it on GitHub<#419 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/ABJ6QODBCRAOVYFS6QQLKTTU2ZD4ZANCNFSM4V4RDOIQ>.
Triage notifications on the go with GitHub Mobile for iOS<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675> or Android<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
-
So the whole direction of the language is to get rid of all the noise and boilerplate. Can someone tell me what the most readable way is to just have a constructed enclosed object that needs some Async initialization in there? I'm getting so tired of everytime writing the same:
while this could have just been:
Yes, you can in theory get rid of the InitAsync - but now you have to refer to the obj constantly to init all the proprties, because you are in a static method. This means suddenly the code doesn't look at a constructor anymore, while practically it is a contructor. Also this looks really ugly and you have to repeat it everywhere in your code:
Code is all about readability. Ask 100 people that are new to the language (free of dogma, like you and me) which of the above is most readable and understandable and I'm pretty sure 95% will answer the async constructor version. I'm open to do a survey about that. I'm also pretty sure they can explain how it works and what to expect. I understand this feature request faces a lot of backlash. Some people are used to work with a lot of abstractions, layers mocks and whatever not, I don't think that a universal language like C# should dictate this onto others. In your own projects you can just tell other people they can't use async constructor like some people forbid to use |
Beta Was this translation helpful? Give feedback.
-
I'm not convinced a language feature is the best solution. There's a fair amount of discussion happening re async startup and service resolution in ASP.NET (see dotnet/aspnetcore#24142 and friends). A library solution may handle the 90% case without making a language change that has far-reaching effects. |
Beta Was this translation helpful? Give feedback.
-
So, there are two fields of thought here about what a constructor is in terms of creating/filling in (i.e., constructing) an object
@TahirAhmadov has been advocating for the former and @DirkBoer for the latter. There's an ideological disconnect, and it butts heads when I/O (or anything asynchronous) is thrown into the mix. After all, why is synchronous I/O acceptable, but not asynchronous I/O? Maybe synchronous I/O isn't acceptable to you, but it's what exists, for better or worse. Hence, this request (and others) for asynchronous constructors. Regardless, as it stands now, the LDM has decided not to entertain this idea, and continuing to argue past each other about it doesn't do anything. |
Beta Was this translation helpful? Give feedback.
-
Moved from dotnet/roslyn#6788
async constructors
Declaration
Usage
Notes
Beta Was this translation helpful? Give feedback.
All reactions