Skip to content

Commit 4e1c413

Browse files
committed
Update README.md
1 parent 2a93919 commit 4e1c413

File tree

1 file changed

+89
-2
lines changed

1 file changed

+89
-2
lines changed

README.md

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,91 @@
1-
ITask
2-
=====
1+
t# ITask
32

43
Provides an awaitable covariant ITask interface which may be used in place of the built-in Task class.
4+
5+
## Purpose
6+
7+
The built-in `System.Threading.Tasks.Task` and `System.Threading.Tasks.Task<TResult>` classes allow for compiler support of the `async` and `await` keywords. However, since these types must be used as the return values of the methods which leverage the support of the keywords, any interface containing one of these methods may not be covariant over the type `TResult`. This can cause a big problems, especially when converting methods on existing interfaces from being synchronous to asynchronous.
8+
9+
This is where the `ITask` interface comes in. Both `ITask` and `ITask<TResult>` interfaces are included for consistency, but the real power lies in teh `ITask<TResult>` interface. It exposes the same functionality as `System.Threading.Tasks.Task<TResult>`, simply through an interface. Because `TResult` is only used in the output position for this interface, it is covariant (its definition is `public interface ITask<out TResult>`) and may be used as a return value within another generic interface without breaking its covariance.
10+
11+
## Usage
12+
13+
### Using an ITask within an interface
14+
15+
Given the following interface:
16+
17+
```c#
18+
interface ITest<out T>
19+
{
20+
T CopmputeValue();
21+
}
22+
```
23+
24+
converting the `ComputeValue` method from being synchronous to ansynchronous would require removing the covariance of interface `ITest<T>` as follows:
25+
26+
```c#
27+
interface ITest<T>
28+
{
29+
System.Threading.Tasks.Task<T> CopmputeValue();
30+
}
31+
```
32+
33+
With the `ITask` interface, it is possible to make the `ComputeValue` method compatible with the `await` keyword (indicating that it is asynchronous) while maintaining covariance by changing the interface as follows:
34+
35+
```c#
36+
interface ITest<out T>
37+
{
38+
ITask<T> CopmputeValue();
39+
}
40+
```
41+
42+
### Awaiting an ITask
43+
44+
The await keyword may be used with an `ITask<TResult>` in the same manner that it is with a `System.Threading.Tasks.Task<TResult>`.
45+
46+
For example, within a method marked with the `async` keyword and given a variable `t` of type `ITest<int>`, the `await` keyword may be used as follows:
47+
48+
```c#
49+
int result = await t.ComputeValue();
50+
```
51+
52+
Note that the functionality required by the compiler to enable `await` keyword support is included in a class in the `MorseCode.ITask` namespace. Therefore, a `using` statement will need to be added to the file for this code to compile as follows:
53+
54+
```c#
55+
using MorseCode.ITask;
56+
```
57+
58+
If you are using Resharper, it should suggest adding this `using` statement automatically.
59+
60+
### Returning an ITask
61+
62+
Unfortunately, the C# compiler only supports marking methods with the `async` keyword if they return a `System.Threading.Tasks.Task` or a `System.Threading.Tasks.Task<TResult>`. However, the only reason to use the `async` keyword is to enable support for the `await` keyword within that method. As long as the method itself returns an awaitable (which `ITask` and `ITask<TResult>` are), then it doesn't matter if that method is marked with the `async` keyword to callers of the method.
63+
64+
However, we do wish to maintain support for the `async` keyword for the code within a method returning either an `ITask` or an `ITask<TResult>`.
65+
66+
The easiest way to accomplish this is to use the `TaskInterfaceFactory` class's `Create` method. This method expects as its only parameter a method returning either a `System.Threading.Tasks.Task` or a `System.Threading.Tasks.Task<TResult>` and produces either an `ITask` or an `ITask<TResult>`. This method may be defined as a lambda and marked with the `async` keyword. The result of the call to `TaskInterfaceFactory.Create` can simply be returned from the method returning the `ITask` or `ITask<TResult>` and should be the only statement within that method.
67+
68+
For example, the following is a sample implementation of the `ComputeValue` method defined above in a class implementing `ITest<int>`:
69+
70+
```c#
71+
public ITask<int> ComputeValue()
72+
{
73+
return TaskInterfaceFactory.Create(async () =>
74+
{
75+
// Do some computing here!
76+
// The await keyword may be used freely.
77+
78+
//For example:
79+
int result1 = await DoSomeOtherComputing();
80+
int result2 = await DoSomeOtherComputingForSomethingElse();
81+
return result1 + result2;
82+
});
83+
}
84+
```
85+
86+
If you already have a `System.Threading.Tasks.Task` or a `System.Threading.Tasks.Task<TResult>` and you wish to convert it into an `ITask` or an `ITask<TResult>` simply use the `AsITask` extension method as follows (given a variable `t` of type `System.Threading.Tasks.Task` and a variable `t2` of type `System.Threading.Tasks.Task<int>`):
87+
88+
```c#
89+
ITask iTask = t.AsITask();
90+
ITask<int> iTask2 = t2.AsITask();
91+
```

0 commit comments

Comments
 (0)