Task.WhenAllが返すTaskオブジェクトのIsCanceled,IsFaultedの値

#問題
`Task.WhenAll`メソッドが返す`Task`オブジェクトは、引数であたえられたコレクション内の`Task`オブジェクトすべてが完了(あるいは例外、中断)すると、`IsCompleted`がtrueになる。
では、`IsCanceled`, `IsFaulted`プロパティはどうなるか。
複数あるタスクのうち、ひとつだけがキャンセルされたときはどうなるか。
このとき、例外をスローしたタスクが混ざっていた場合はどうか。

#検証方法
以下のコードで調査をおこなった。

```csharp
static void Main(string[] args)
{
	Console.WriteLine("Empty");
	Action(new Task[0]);
	Console.WriteLine("");

	Console.WriteLine("Completed task");
	Action(new[] { CreateNormalTask() });
	Console.WriteLine("");

	Console.WriteLine("Canceled task");
	Action(new[] { CreateCanceledTask() });
	Console.WriteLine("");

	Console.WriteLine("Faulted task");
	Action(new[] { CreateFaultedTask() });
	Console.WriteLine("");

	Console.WriteLine("Completed task & Canceled task");
	Action(new[] { CreateNormalTask(), CreateCanceledTask() });
	Console.WriteLine("");

	Console.WriteLine("Complated task & Fauleted task");
	Action(new[] { CreateNormalTask(), CreateFaultedTask() });
	Console.WriteLine("");

	Console.WriteLine("Canceled task & Fauleted task");
	Action(new[] { CreateCanceledTask(), CreateFaultedTask() });
	Console.WriteLine("");

	Console.WriteLine("Completed task & Canceled task & Fauleted task");
	Action(new[] { CreateNormalTask(), CreateCanceledTask(), CreateFaultedTask() });
	Console.WriteLine("");

	Console.ReadKey();
}

static void Action(Task[] tasks)
{
	var whenAllTask = Task.WhenAll(tasks);
	Console.WriteLine($"WhenAllTask: IsCompleted = {whenAllTask.IsCompleted}, IsCanceled = {whenAllTask.IsCanceled}, IsFaulted = {whenAllTask.IsFaulted}");

	foreach (var pair in tasks.Select((task, index) => new {task, index}))
		Console.WriteLine($"  {pair.index}: IsCompleted = {pair.task.IsCompleted}, IsCanceled = {pair.task.IsCanceled}, IsFaulted = {pair.task.IsFaulted}");
}

static Task CreateNormalTask() => Task.CompletedTask;
static Task CreateCanceledTask()
{
	var source = new TaskCompletionSource<int>();
	source.TrySetCanceled();
	return source.Task;
}
static Task CreateFaultedTask()
{
	var source = new TaskCompletionSource<int>();
	source.TrySetException(new Exception());
	return source.Task;
}
```

#実行結果一覧

| 正常終了したタスク | キャンセルされたタスク | 例外をスローしたタスク | IsCompleted |  IsCanceled | IsFaulted |
|:---:|:---:|:---:|:---:|:---:|:---:|
|あってもなくても|なし|なし|true|false|false|
|あってもなくても|あり|なし|true|true|false|
|あってもなくても|あってもなくても|あり|true|false|true|


詳細は以下のとおり。

```
Empty
WhenAllTask: IsCompleted = True, IsCanceled = False, IsFaulted = False

Completed task
WhenAllTask: IsCompleted = True, IsCanceled = False, IsFaulted = False
  0: IsCompleted = True, IsCanceled = False, IsFaulted = False

Canceled task
WhenAllTask: IsCompleted = True, IsCanceled = True, IsFaulted = False
  0: IsCompleted = True, IsCanceled = True, IsFaulted = False

Faulted task
WhenAllTask: IsCompleted = True, IsCanceled = False, IsFaulted = True
  0: IsCompleted = True, IsCanceled = False, IsFaulted = True

Completed task & Canceled task
WhenAllTask: IsCompleted = True, IsCanceled = True, IsFaulted = False
  0: IsCompleted = True, IsCanceled = False, IsFaulted = False
  1: IsCompleted = True, IsCanceled = True, IsFaulted = False

Complated task & Fauleted task
WhenAllTask: IsCompleted = True, IsCanceled = False, IsFaulted = True
  0: IsCompleted = True, IsCanceled = False, IsFaulted = False
  1: IsCompleted = True, IsCanceled = False, IsFaulted = True

Canceled task & Fauleted task
WhenAllTask: IsCompleted = True, IsCanceled = False, IsFaulted = True
  0: IsCompleted = True, IsCanceled = True, IsFaulted = False
  1: IsCompleted = True, IsCanceled = False, IsFaulted = True

Completed task & Canceled task & Fauleted task
WhenAllTask: IsCompleted = True, IsCanceled = False, IsFaulted = True
  0: IsCompleted = True, IsCanceled = False, IsFaulted = False
  1: IsCompleted = True, IsCanceled = True, IsFaulted = False
  2: IsCompleted = True, IsCanceled = False, IsFaulted = True
```

# 結論

`Task.WhenAll`が返すタスクについて、`IsComplated`がtrueのとき、

- `IsCanceled`の値では、キャンセルされたタスクがないことはわからない。例外をスローしたタスクがひとつでもあると、必ずfalseになる。
- `IsFaulted`の値によって、例外をスローしたタスクがあるかどうかがわかる