Task.FromResult の使い方と活用方法

# Task.FromResult<T> の使い方と活用方法

C# で非同期プログラミングをする際、 `Task.FromResult<T>` は非常に便利なメソッドです。本記事では `Task.FromResult<T>` の基本的な使い方や実際の活用方法、さらには適切な場面と注意点について詳しく解説します。

---

## 1. `Task.FromResult<T>` とは？

### `Task.FromResult<T>` の基本

`Task.FromResult<T>` は、指定した値を返す **すでに完了している `Task<T>` を即座に作成** するための静的メソッドです。

```csharp
Task<T> Task.FromResult<T>(T result)
```

これにより、非同期メソッドの戻り値として `Task<T>` を求められた場合でも、明示的に `async`/`await` を使わずに結果を即座に返すことができます。

例えば、以下のコードは `Task<int>` を即座に返します。

```csharp
Task<int> task = Task.FromResult(42);
```

この `task` はすでに完了しており、`await` するとすぐに `42` を得ることができます。


---

## 2. `Task.FromResult<T>` を使うべき場面

### ✅ **(1) 非同期メソッドで即座に結果を返したい場合**

非同期メソッド (`async` メソッド) の戻り値が `Task<T>` である場合、処理結果がすぐに分かっているなら `Task.FromResult<T>` を使うことで、オーバーヘッドを減らし効率を上げることができます。

#### **例：キャッシュデータがある場合**

```csharp
private Dictionary<int, string> _cache = new();

public Task<string> GetDataAsync(int id)
{
    if (_cache.TryGetValue(id, out var cachedData))
    {
        return Task.FromResult(cachedData); // キャッシュがあれば即座に返す
    }
    return FetchFromDatabaseAsync(id); // キャッシュがなければDBから取得
}
```

この方法により、キャッシュがある場合は `Task.Run` のような不要なスレッド生成をせず、即座に結果を返すことができます。

---

### ✅ **(2) 非同期メソッドのモックを作成する場合**

`Task.FromResult<T>` は、**ユニットテストで非同期メソッドの戻り値をモックする** のにも適しています。

#### **例：Moq を使った非同期メソッドのモック**

```csharp
var mockService = new Mock<IMyService>();
mockService.Setup(s => s.GetValueAsync()).Returns(Task.FromResult("Mocked Value"));
```

これにより、非同期メソッド `GetValueAsync` が呼ばれた際に、実際の処理をせず即座に `"Mocked Value"` を返すようになります。これによってテストの実行速度を向上させることができます。

---

### ✅ **(3) インターフェースのシグネチャを統一する場合**

`Task.FromResult<T>` を使用すると、**同期的に完了する処理を非同期メソッドの形にする** ことができます。

#### **例：非同期インターフェースの実装**

```csharp
public interface IUserService
{
    Task<User> GetUserAsync(int id);
}

public class MockUserService : IUserService
{
    public Task<User> GetUserAsync(int id)
    {
        return Task.FromResult(new User { Id = id, Name = "Test User" });
    }
}
```

このように、同期的な処理を `Task<T>` でラップすることで、非同期APIとの統一性を保つことができます。

---

## 3. `Task.FromResult<T>` の注意点

### ❌ **(1) 長時間の処理には向かない**

`Task.FromResult<T>` は **非同期処理を実行するわけではない** ため、長時間の処理には適しません。そのような場合は `Task.Run` を使用するのが適切です。

#### **悪い例：重い処理を `Task.FromResult` で返す**

```csharp
public Task<int> GetSlowResultAsync()
{
    return Task.FromResult(SlowComputation()); // メインスレッドをブロックする
}
```

上記の `SlowComputation()` が重い処理である場合、メインスレッドをブロックするため、**非同期処理のメリットが失われます。**

#### **適切な方法：`Task.Run` を使う**

```csharp
public Task<int> GetSlowResultAsync()
{
    return Task.Run(() => SlowComputation()); // 別スレッドで実行
}
```

---

### ❌ **(2) 例外処理に注意**

`Task.FromResult<T>` を使うと、**発生した例外は即座にスローされる** ため、例外を非同期に伝播させる場合は `Task.FromException` を使うべきです。

#### **例：非同期メソッドでエラーを返す場合**

```csharp
public Task<int> GetErrorAsync()
{
    return Task.FromException<int>(new InvalidOperationException("エラーが発生しました"));
}
```

こうすることで、非同期処理において `await` した際に `try-catch` で適切に例外を処理できます。

---

## 4. まとめ

| **用途** | **適切な方法** |
|----------|-----------|
| **即座に結果を返す** | `Task.FromResult(42)` を使用 |
| **キャッシュがある場合** | `Task.FromResult` でキャッシュデータを返す |
| **非同期メソッドのモック** | `Returns(Task.FromResult(value))` を使う |
| **非同期APIのシグネチャ統一** | `Task.FromResult` を活用 |
| **長時間の処理には不向き** | `Task.Run` を使用する |
| **エラーを非同期に伝播** | `Task.FromException<T>` を使う |

`Task.FromResult<T>` を適切に活用することで、パフォーマンスとコードの可読性を向上させることができます。適切な場面で使い分けるようにしましょう！