The part about FrozenSet and FrozenDictionary is a misconception. Those two collections were not added because they are immutable, you already have ImmutableHashSet and ImmutableDictionary. You should keep using those in most circumstances. The Frozen ones are meant to be used when they are are created on startup, kept in memory, and repeatedly used afterwards. The creation cost is way higher, but the reading part is significantly faster, so they are not suitable for operations that create them on the fly and discard them after they are finished.
I didn't want to watch the whole video to see if Brandon Minnick changed anything, but I've seen an similar video from him over the years with the same misguided info. Here's the last one; my complaints are the top comment: th-cam.com/video/zhCRX3B7qwY/w-d-xo.html. Wish I had this guys confidence in being wishy-washy with the details.
Totally fair feedback. You're absolutely right - there is a higher cost to create `FrozenSet` than `IReadOnlyList` and `ImmutableList`. When running benchmarks on my app, I found that the additional performance cost was around 1ms. For this app, it's not much of a performance hit; for example, I'm not dropping any frames due to using `FrozenSet`. That being said, I appreciate the feedback and I will update my recommendations for using `FrozenSet` to include this caveat 💯
Amazing how the same presentation is given over and over with the same errors and misconceptions. 5 months after the previous one, the frozen collections and thread switching misinformation is still there
@@gregh2327 I agree. I've seen that video last year also, and now TH-cam suggests me this one. I just came here knowing for sure that a comment(s) pointing he is wrong somewhere will exist. Here they are. Now I am pretty sure the video is not worth watching
50:40 SuppressThrowing is useful when you need to wait for all tasks being finished. This is not what WhenAll does by default, because it does not wait any longer if some Task throws. Js has Promise.All and Promise.AllSettled for that and finally we can achieve the same in a concise way in C#.
Awesome presentation. Thanks for showing the iasynenumerable idea . I saw it somewhere, used it, lost the code, and couldn't find it again. Gonna go back to using this. HERO!
It doesn't feel right to put work in the constructor. Especially when you need to create a lot of view models at once for say a list . I usually have some other type of async Task method to initialize work it so I or my team don't need to remember some extension method I made a year ago to handle doing work in the constructor safely. You also have to be very careful with using ConfigureAwait(false) before a collection clear. The ViewModel should have little to no knowledge of what or how the UI is bound to it. A UI team member may bind a list control to the Collection that is being cleared here. However if you add a ConfigureAwait(false) in the call before and the execution continues in a thread that is not the UI thread when the collection clear occurs it will fire change notification events in a different thread from the UI. This will crash the app as the control bound to the collection cannot be modified from a non-ui thread.
Agreed. In the .NET MAUI apps I've published to the app stores, I trigger this logic using the `OnAppearing()` method. But, for this talk it's a quick + dirty example to segueway into the pros + cons of `async void`.
Be weary of async apis that don’t provide a means of cancellation. Using the workaround described here with WaitAsync will not actually cancel the operation, it will simply cause the task to complete via cancellation while the main operation continues. This can lead to all kinds of problems.
Great talk. Quick question though. Why would you do work in a constructor at all? Constructors initialize memory, methods do logic. Most coding platforms I worked with (winforms, wpf, asp,etc) have a 'starting' method or event that you can use so that your constructor doesn't need any weird 'safe way to execute async void code'. For instance, even though you created this safe approach to async void, you still cannot handle the error in a meaningful way (eg, showing it to the user). So, instead you choose the only left option which is to trace it out. Why would you do it in that way?
Some like the "init" qualifier for properties because they allow for flexibility while retaining safety. Unfortunately that initialization can't be deferred to an init-method (unless you call it in the constructor). Its also a good thing to have the object in the expected state whenever a check for "is not null" or "is " returns true, and there's a potential opening between ctor --> init-method for any such checks to occur, where some properties may not have been initiated. That would open the can of worms of locking etc.
Yup - agreed, I don't recommend triggering any async code in the constructor. In the .NET MAUI apps I publish to the app stores, I trigger this logic using the `OnAppearing()` method, not in the constructor. But, for this talk it's a quick + dirty example to segueway into the pros + cons of `async void`. To handle the exception using `.SafeFireForget()`, you can pass in an Excecption Handler: `.SafeFireAndForget(ex => Trace.WriteLine(ex))`
Doesn't WaitAsync re-create the conditions of async void in case of a cancellation? I would expect the work in the initial task to keep going, because it does not observe the token. And if it raises an exception, there is no longer anything to observe it. Am I missing something?
@@gregh2327 If there's any innacuraces in the video, please do let me know! I've iterated this talk over the last 8 years and have certainly made improvements based on the feedback I receive. It's never my intention to mislead anyone.
@@brandonminnick Thank you for responding and being open. I unfortunately don't have time to watch and do a write up; but a couple comments in here are astute. In previous talks, my frustration has been regarding discrepancies in how the CLR works and how you describe it working.
In the first example, there will be no thread switching or even asynchronous work done. You call async code and immediatelly await on it. It is the exact same code as if you did response = httpClient.Get(...); stream = response.Content.ReadAsStream();, etc. If you want to demonstrate anynchronous code, you need to show asynchronous code. This, is not.
The part about FrozenSet and FrozenDictionary is a misconception. Those two collections were not added because they are immutable, you already have ImmutableHashSet and ImmutableDictionary. You should keep using those in most circumstances. The Frozen ones are meant to be used when they are are created on startup, kept in memory, and repeatedly used afterwards. The creation cost is way higher, but the reading part is significantly faster, so they are not suitable for operations that create them on the fly and discard them after they are finished.
I didn't want to watch the whole video to see if Brandon Minnick changed anything, but I've seen an similar video from him over the years with the same misguided info. Here's the last one; my complaints are the top comment: th-cam.com/video/zhCRX3B7qwY/w-d-xo.html. Wish I had this guys confidence in being wishy-washy with the details.
Totally fair feedback. You're absolutely right - there is a higher cost to create `FrozenSet` than `IReadOnlyList` and `ImmutableList`. When running benchmarks on my app, I found that the additional performance cost was around 1ms. For this app, it's not much of a performance hit; for example, I'm not dropping any frames due to using `FrozenSet`. That being said, I appreciate the feedback and I will update my recommendations for using `FrozenSet` to include this caveat 💯
Amazing how the same presentation is given over and over with the same errors and misconceptions. 5 months after the previous one, the frozen collections and thread switching misinformation is still there
@@gregh2327 I agree. I've seen that video last year also, and now TH-cam suggests me this one. I just came here knowing for sure that a comment(s) pointing he is wrong somewhere will exist. Here they are. Now I am pretty sure the video is not worth watching
Great voice, great tempo and super captivating. Fantastic listen
Thanks so much!
THIS WAS AWESOME.. RIGHT ON POINT. NOT TOO LONG, NOT TOO SHORT. GREAT JOB, THANKS..
50:40 SuppressThrowing is useful when you need to wait for all tasks being finished. This is not what WhenAll does by default, because it does not wait any longer if some Task throws. Js has Promise.All and Promise.AllSettled for that and finally we can achieve the same in a concise way in C#.
Awesome presentation. Thanks for showing the iasynenumerable idea . I saw it somewhere, used it, lost the code, and couldn't find it again. Gonna go back to using this. HERO!
Thanks! You're most welcome!
Really quality material. Thank you for this!
Great talk!
Thanks!!
It doesn't feel right to put work in the constructor. Especially when you need to create a lot of view models at once for say a list . I usually have some other type of async Task method to initialize work it so I or my team don't need to remember some extension method I made a year ago to handle doing work in the constructor safely.
You also have to be very careful with using ConfigureAwait(false) before a collection clear. The ViewModel should have little to no knowledge of what or how the UI is bound to it. A UI team member may bind a list control to the Collection that is being cleared here. However if you add a ConfigureAwait(false) in the call before and the execution continues in a thread that is not the UI thread when the collection clear occurs it will fire change notification events in a different thread from the UI. This will crash the app as the control bound to the collection cannot be modified from a non-ui thread.
Agreed. In the .NET MAUI apps I've published to the app stores, I trigger this logic using the `OnAppearing()` method. But, for this talk it's a quick + dirty example to segueway into the pros + cons of `async void`.
Good summary of what is known for years.
Very engaging. Thank you.
46:36 Async/Await Best Practices
Good grief, I did not know that await foreach or AsyncEnumerable existed. I see it now, in
MS Learn -> C# Documentation -> Concepts -> Iterators
Be weary of async apis that don’t provide a means of cancellation. Using the workaround described here with WaitAsync will not actually cancel the operation, it will simply cause the task to complete via cancellation while the main operation continues. This can lead to all kinds of problems.
Great talk. Quick question though. Why would you do work in a constructor at all? Constructors initialize memory, methods do logic. Most coding platforms I worked with (winforms, wpf, asp,etc) have a 'starting' method or event that you can use so that your constructor doesn't need any weird 'safe way to execute async void code'.
For instance, even though you created this safe approach to async void, you still cannot handle the error in a meaningful way (eg, showing it to the user). So, instead you choose the only left option which is to trace it out. Why would you do it in that way?
Some like the "init" qualifier for properties because they allow for flexibility while retaining safety. Unfortunately that initialization can't be deferred to an init-method (unless you call it in the constructor). Its also a good thing to have the object in the expected state whenever a check for "is not null" or "is " returns true, and there's a potential opening between ctor --> init-method for any such checks to occur, where some properties may not have been initiated. That would open the can of worms of locking etc.
@@djupstaten2328But why can't we have private constructor, and have public async factory method?
@@djupstaten2328 yes why don't just use Onload / Init method?
@@TheRPGminer you can, but this more of a self triggerred/invoked operations where you just creata an instance from a factory and it kicks in
Yup - agreed, I don't recommend triggering any async code in the constructor. In the .NET MAUI apps I publish to the app stores, I trigger this logic using the `OnAppearing()` method, not in the constructor. But, for this talk it's a quick + dirty example to segueway into the pros + cons of `async void`.
To handle the exception using `.SafeFireForget()`, you can pass in an Excecption Handler: `.SafeFireAndForget(ex => Trace.WriteLine(ex))`
For the issue with intelisense there is a recommendation to suffix async methods with Async. Not my favorit but in this case it would help
it was cool, ty
Doesn't WaitAsync re-create the conditions of async void in case of a cancellation? I would expect the work in the initial task to keep going, because it does not observe the token. And if it raises an exception, there is no longer anything to observe it. Am I missing something?
the nervous manner of speaking is kind of irritating. otherwise - great presentation
ConfigureAwait being true by default was a dumb desicion.
I initially disagreed, but now I'm on the fence. I think you'd find an equal number of people that disagree.
It was the right default at the time. That’s no longer the case
this feels like the same video as this one:
th-cam.com/video/zhCRX3B7qwY/w-d-xo.html
Again?
Is it still inaccurate?
@@gregh2327 If there's any innacuraces in the video, please do let me know! I've iterated this talk over the last 8 years and have certainly made improvements based on the feedback I receive. It's never my intention to mislead anyone.
@@brandonminnick Thank you for responding and being open. I unfortunately don't have time to watch and do a write up; but a couple comments in here are astute. In previous talks, my frustration has been regarding discrepancies in how the CLR works and how you describe it working.
In the first example, there will be no thread switching or even asynchronous work done. You call async code and immediatelly await on it. It is the exact same code as if you did response = httpClient.Get(...); stream = response.Content.ReadAsStream();, etc. If you want to demonstrate anynchronous code, you need to show asynchronous code. This, is not.
What would be a demonstration of asynchronous code?
The whole .NET is a mistake ... let alone async