Tuesday, April 2, 2019

Task.IsCompletedSuccessfully - YAY! (and hmmm)

So, apparently I missed something along the way. Task in C# has an "IsCompletedSuccessfully" property (in some environments). This has been around since .NET Core 2.0, and it is in the current 2.2 release (and the 3.0 preview).
The reason I love the idea of "IsCompletedSuccessfully" is that "IsCompleted" does not mean what you think it means.
But there are some caveats about which types of projects can use this property. More on that below.

The Problem of "IsCompleted"
I've been writing and speaking about Task and Await since 2015. It's one of those things that took me a while to understand and then became really useful to me.

One of the hurdles I had to deal with has to do with the "Is" properties on Task. These include "IsCanceled", "IsFaulted", and "IsCompleted".


The main problem with these properties is that they sound like they are mutually exclusive, meaning something will be *either* Canceled *or* Faulted *or* Completed. And that is not the case.

If we look at the documentation for "IsCompleted", we find the following remarks:


IsCompleted will return true when the task is in one of the three final states: RanToCompletion, Faulted, or Canceled.

So "IsCompleted" does not mean "completed successfully", it means "no longer running".

The usefulness of this property in Task continuations is limited. And because of the problems I had when I first came across it, I have talked about it in articles (Task Continuations), videos (Task and Await in C# - Part 3), and also in my live presentations (I'll Get Back to You: Task, Await, and Asynchronous Methods).

Note: the short version is that if we want to see if a Task completed successfully, we can use the Task.Status property (an enumeration) and look for "RanToCompletion".

With .NET Core 2.0 and later, we finally get the option that I've been waiting for.

IsCompletedSuccessfully
The "IsCompletedSuccessfully" property has been around since .NET Core 2.0. Somehow I missed it. It wasn't really a surprise that I missed it because I was mainly using Task in WPF projects (.NET Framework) and .NET Standard 2.0 class libraries. This property is not available in those environments (more on that in a bit).

First, let's look at the documentation (Task.IsCompletedSuccessfully Property):


The first thing to note is that the documentation is currently incomplete. It just tells us that the property exists, but it doesn't say what it is for. Based on previous experience (and a little experimentation), we can deduce that this tells us whether the task ended up in the "RanToCompletion" status.

Also note that this applies to .NET Core only. It has been available since .NET Core 2.0, and it is available in 2.1, 2.2, and the 3.0 Preview.

So, Yay! that this is available, and hmmm... let's figure out where we can really use this.

.NET Core vs. .NET Standard
The first thing to consider is that there is a difference between .NET Core and .NET Standard. With .NET Standard, we can create libraries that will compile in a variety of environments. I have created lots of .NET Standard libraries that can be used with WPF applications (using .NET Framework) and WebAPI applications (using .NET Core).

This is great because .NET Standard gives us a standard set of APIs that can be used by any project that supports that version of .NET Standard.

I've been using .NET Standard 2.0. This means that my libraries will work with .NET Core 2.0 (and above) and .NET Framework 4.6.1 (but more realistically, they work with .NET Framework 4.7.2 -- but that's another story).

The bad news is that "IsCompletedSuccessfully" is *not* available in .NET Standard 2.0.

This means that for my current projects using .NET Standard 2.0 and WPF with .NET Framework 4.7.2, this property is not available.

If we do want to use "IsCompletedSuccessfully" in a class library, we also have the option of creating a .NET Core class library. This has the restriction that we can only call it from a .NET Core project, so it will not work with a .NET Framework project like our .NET Standard libraries do.

The good news is that "IsCompletedSuccessfully" *is* available in .NET Standard 2.1.

It's important to understand that .NET Standard 2.1 is not really available yet. This will come along with .NET Core 3.0 which is still in preview.

To make things more "interesting", .NET Standard 2.1 will not be supported by .NET Framework 4.8. Here's an excerpt from Microsoft: "Announcing .NET Standard 2.1".
"Given many of the API additions in .NET Standard 2.1 require runtime changes in order to be meaningful, .NET Framework 4.8 will remain on .NET Standard 2.0 rather than implement .NET Standard 2.1. .NET Core 3.0 as well as upcoming versions of Xamarin, Mono, and Unity will be updated to implement .NET Standard 2.1."
So, we will not be able to use this property with .NET Framework 4.8.

Can I Use "IsCompletedSuccessfully"?
  • .NET Core 3.0: YES
  • .NET Standard 2.1: YES
  • .NET Core 2.0+ : YES
  • .NET Standard 2.0: No
  • .NET Framework 4.7.2: No
  • .NET Framework 4.8: No

Moving to .NET Core 3.0
With all of the changes that are happening in the .NET ecosystem, it is becoming more and more clear that we should be moving to .NET Core. For 2 examples, (1) .NET Framework 4.8 will not support .NET Standard 2.1, and (2) several C# 8 features will not be implemented in .NET Framework 4.8.

.NET Core 3.0 supports WinForms and WPF applications. I've done a few experiments with the Preview of .NET Core 3.0, and it works pretty well for the WPF applications that I've converted.

If we start to move to .NET Core, then things get easier in a lot of areas. The project systems are more compatible, we get runtime enhancements, and we can use .NET Standard 2.1.

We are in bit of a transitional period here. .NET Core 3.0 is still to be released. And even though Visual Studio 2019 works well with the .NET Core 3.0 Preview, the tooling for WinForms and WPF does not exist yet. There are some ways that we can manage WPF projects today, and I'll show that in an upcoming article.

If you haven't done so already, take a look at .NET Core. This is the future of .NET.

Happy Coding!

3 comments:

  1. Is it true that:
    IsCompletedSuccessfully = IsCompleted && !(IsCanceled && IsFaulted)
    ??

    ReplyDelete
  2. Sorry. Should be:
    IsCompletedSuccessfully = IsCompleted && !(IsCanceled || IsFaulted)

    ReplyDelete
    Replies
    1. That is how I understand it. If IsCompletedSuccessfully is true, then IsCompleted is true, IsCanceled is false, and IsFaulted is false.

      Delete