RJCP.Threading 0.2.1

dotnet add package RJCP.Threading --version 0.2.1                
NuGet\Install-Package RJCP.Threading -Version 0.2.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RJCP.Threading" Version="0.2.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add RJCP.Threading --version 0.2.1                
#r "nuget: RJCP.Threading, 0.2.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install RJCP.Threading as a Cake Addin
#addin nuget:?package=RJCP.Threading&version=0.2.1

// Install RJCP.Threading as a Cake Tool
#tool nuget:?package=RJCP.Threading&version=0.2.1                

RJCP Thread Library

The RJCP.Threading library was introduced due to a problem with .NET not having an implementation of ITask<T> which allows for covariant interfaces.

It is based on the information by Extending the Async Methods in C#.

1. Why is this Library needed

.NET 4.x introduced the Task<T> type, which represents operations that can run on different contexts that return values at some later point in time. In .NET 4.5, they keyword async and await were introduced that provide an easy to read asynchronous programming model with the language realizing this in the background using state machines.

While this model brought significant improvements to the language, it was no longer possible to define interfaces using covariant types. For example, the language does not allow:

namespace RJCP.Threading.Tasks.Covariance
{
    public interface ILineReader<out T> where T : ILine
    {
        Task<T> GetLineAsync();
    }
}

The code above will not compile, unless the out keyword is removed. This prevents code such as the following from being written:

namespace RJCP.Threading.Tasks
{
    using System;
    using System.Threading.Tasks;

    public interface ILine {
        string Text { get; }
    }

    public interface ILineReader<T> where T : ILine {
        Task<T> GetLineAsync();
    }

    public class Line : ILine {
        public string Text { get { return "Text"; } }
    }

    public class LineReader : ILineReader<Line> {
        public async Task<Line> GetLineAsync() {
            await Task.Delay(1);
            return new Line();
        }
    }

    public static class LineModule {
        public static async Task PrintLine() {
            // This line has the error:
            //  CS0266: Cannot implicitly convert type 'RJCP.Threading.Tasks.LineReader'
            //  to 'RJCP.Threading.Tasks.ILineReader<RJCP.Threading.Tasks.ILine>'. An
            //  explicit conversion exists (are you missing a cast?)
            ILineReader<ILine> reader = new LineReader();
            ILine line = await reader.GetLineAsync();

            Console.WriteLine("{0}", line.Text);
        }
    }
}

The error is raised because the interface ILineReader doesn't have a covariant type T. It must be ILineReader<Line> for the code to compile. But then if you have a new class LineReader2 that has T : Line2 : ILine, it can't be assigned to reader as the types are again incompatible.

Features

2. Introducing an Interface to allow Covariance

However, with the existence of an interface ITask, we can now make the type T covariant, and the method works as expected.

namespace RJCP.Threading.Tasks {
    using System;
    using System.Threading.Tasks;

    public interface ILine {
        string Text { get; }
    }

    public interface ILineReader<out T> where T : ILine {
        ITask<T> GetLineAsync();
    }

    public class Line : ILine {
        public string Text { get { return "Text"; } }
    }

    public class LineReader : ILineReader<Line> {
        public async ITask<Line> GetLineAsync() {
            await Task.Delay(1);
            return new Line();
        }
    }

    public static class LineModule {
        public static async ITask PrintLine() {
            ILineReader<ILine> reader = new LineReader();
            ILine line = await reader.GetLineAsync();

            Console.WriteLine("{0}", line.Text);
        }
    }
}

Where possible, avoid the usage of the ITask as it is slower.

Task Group

A TaskGroup is a simple collection to reduce boiler-plate code when waiting on multiple tasks.

Create a TaskGroup and RegisterTask(Task) to have the task group be able to wait on the tasks.

2. Release History

2.1. Version 0.2.1

Bugfixes:

  • TaskGroup: Remove asynchronous behaviour of TaskCompletionSource (fails on Linux) (DOTNET-970)

Quality:

  • Add README.md reference to NuGet package (DOTNET-815)
  • Tasks: Fix unnecessary guard around HashSet (DOTNET-833)
  • Upgrade from .NET Standard 2.1 to .NET 6.0 (DOTNET-936, DOTNET-941, DOTNET-942)

2.2. Version 0.2.0

  • Initial Version
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net6.0

    • No dependencies.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.2.1 140 3/9/2024
0.2.0 150 6/9/2023