Task.FromResult の使い方と活用方法
C# で非同期プログラミングをする際、 Task.FromResult<T> は非常に便利なメソッドです。本記事では Task.FromResult<T> の基本的な使い方や実際の活用方法、さらには適切な場面と注意点について詳しく解説します。
1. Task.FromResult<T> とは?
Task.FromResult<T> の基本
Task.FromResult<T> は、指定した値を返す すでに完了している Task<T> を即座に作成 するための静的メソッドです。
Task<T> Task.FromResult<T>(T result)
これにより、非同期メソッドの戻り値として Task<T> を求められた場合でも、明示的に async/await を使わずに結果を即座に返すことができます。
例えば、以下のコードは Task<int> を即座に返します。
Task<int> task = Task.FromResult(42);
この task はすでに完了しており、await するとすぐに 42 を得ることができます。
2. Task.FromResult<T> を使うべき場面
✅ (1) 非同期メソッドで即座に結果を返したい場合
非同期メソッド (async メソッド) の戻り値が Task<T> である場合、処理結果がすぐに分かっているなら Task.FromResult<T> を使うことで、オーバーヘッドを減らし効率を上げることができます。
例:キャッシュデータがある場合
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 を使った非同期メソッドのモック
var mockService = new Mock<IMyService>();
mockService.Setup(s => s.GetValueAsync()).Returns(Task.FromResult("Mocked Value"));
これにより、非同期メソッド GetValueAsync が呼ばれた際に、実際の処理をせず即座に "Mocked Value" を返すようになります。これによってテストの実行速度を向上させることができます。
✅ (3) インターフェースのシグネチャを統一する場合
Task.FromResult<T> を使用すると、同期的に完了する処理を非同期メソッドの形にする ことができます。
例:非同期インターフェースの実装
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 で返す
public Task<int> GetSlowResultAsync()
{
    return Task.FromResult(SlowComputation()); // メインスレッドをブロックする
}
上記の SlowComputation() が重い処理である場合、メインスレッドをブロックするため、非同期処理のメリットが失われます。
適切な方法:Task.Run を使う
public Task<int> GetSlowResultAsync()
{
    return Task.Run(() => SlowComputation()); // 別スレッドで実行
}
❌ (2) 例外処理に注意
Task.FromResult<T> を使うと、発生した例外は即座にスローされる ため、例外を非同期に伝播させる場合は Task.FromException を使うべきです。
例:非同期メソッドでエラーを返す場合
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> を適切に活用することで、パフォーマンスとコードの可読性を向上させることができます。適切な場面で使い分けるようにしましょう!