C#コンパイラエラーCS1994について解説:async修飾子の使い方と修正方法
CS1994はC#で、非具象メソッドやインターフェイス内のメソッド宣言にasync修飾子を使用した場合に発生するコンパイルエラーです。
メソッド本体が存在しないため、非同期処理の状態管理が行えずエラーとなります。
解決するには、async修飾子を削除するか、メソッド本体を実装してください。
エラー発生の背景と原因
async修飾子の役割と基本動作
async
修飾子は、非同期処理を簡単に実装するために用いられます。
これをメソッドに付与すると、コンパイラはそのメソッド内に存在する await
演算子を検出し、メソッドを状態機械に変換して、非同期処理の継続や中断を管理できるようにします。
具体的には、非同期処理中に中断可能なポイントである await
により、処理の一時停止や再開が可能となり、UI スレッドのブロッキングを防ぐなど、効率的な並行処理を実現します。
例えば、以下のサンプルコードでは、async
修飾子を使うことで、重い処理を別スレッドで実行し、結果が得られるまで await
演算子で待機する処理を記述しています。
using System;
using System.Threading.Tasks;
class Program
{
// 非同期メソッド。await演算子によって状態機械が生成されます。
static async Task Main(string[] args)
{
// Task.Runにより重い処理を非同期で実行
int result = await LongRunningOperation();
Console.WriteLine("結果: " + result);
}
// サンプルの非同期処理
static async Task<int> LongRunningOperation()
{
// 疑似的な重い処理をシミュレートするためのディレイ
await Task.Delay(1000);
// 処理が完了したら整数を返却
return 42;
}
}
結果: 42
\( このように、async
修飾子があるとコンパイラは非同期状態機械を生成し、各 await
で示された継続処理を管理する \)
非具象メソッドへの適用制約
async
修飾子は、メソッド本体の実装が存在する場合にのみ利用可能です。
インターフェイスのメソッドや抽象クラスの抽象メソッドなど、メソッド本体が存在しないケースで async
を使用すると、コンパイラは状態機械を生成できません。
そのため、メソッド本体がないときに async
修飾子を付与するとコンパイラエラー CS1994 が発生します。
この制約は、状態機械の生成がメソッド本体の処理に依存しているため、あらかじめメソッド実装が存在しない場合は、async
を付与する意味がないために設けられています。
CS1994エラーの発生事例
インターフェイス内でのasync修飾子使用例
インターフェイスは、実装のないメソッド宣言を含みます。
そのため、インターフェイス内で async
修飾子を付与すると、メソッド本体が存在しない状態なので、コンパイラは状態機械を生成することができず、CS1994 エラーが発生します。
以下に、エラーが発生するサンプルコードを示します。
// 非具象メソッドでasync修飾子を使用するとCS1994エラーが発生
interface IExample
{
async void Process(); // ここでCS1994エラー
}
メソッド本体が存在しない場合の動作
上記のように、インターフェイス内のメソッドは実装がなく、その宣言だけとなっています。
そのため、async
修飾子を付与しても、コンパイラは状態機械の生成に必要なメソッド本体のロジックが欠如していると判断し、エラーを報告します。
エラー文にある「async
修飾子は、本体があるメソッドでのみ使用できます」というメッセージはこの理由を示しています。
エラー修正方法の解説
async修飾子の削除による修正方法
インターフェイスのようにメソッド本体のない宣言においては、async
修飾子は不要です。
そのため、エラーを解決するには、シンプルに async
を削除する方法があります。
以下に修正例を示します。
// 修正後:非具象メソッドからasync修飾子を削除
interface IExample
{
void Process();
}
メソッド本体実装を追加する方法
非具象メソッドに実装を追加することで、async
修飾子の使用が可能になります。
この場合、インターフェイスに既定実装 (default implementation) を用いて、メソッド本体を定義します。
以下は、メソッド本体を追加することで CS1994 エラーを回避するサンプルコードです。
using System;
using System.Threading.Tasks;
// C# 8.0以降では、インターフェイスに既定実装を記述可能
interface IExample
{
// 既定実装として非同期処理を定義
async void Process()
{
await Task.Run(() =>
{
// 処理をシミュレートするための遅延
Console.WriteLine("非同期処理中");
});
}
}
class Program
{
static void Main(string[] args)
{
IExample example = new ExampleImplementation();
// インターフェイスの既定実装が呼び出される
example.Process();
// エンターキー待機してコンソール出力を確認
Console.ReadLine();
}
}
// インターフェイスを実装するクラス
class ExampleImplementation : IExample { }
非同期処理中
C# 8.0以降の既定具象メソッドの利用方法
C# 8.0以降からは、インターフェイス内に既定具象メソッド (default interface methods) を実装できるようになりました。
この機能により、インターフェイスであってもメソッド本体が存在する場合は async
修飾子の使用が可能となります。
上記のサンプルでも示した通り、インターフェイス内にメソッド本体を記述することで、async
修飾子を適用して非同期処理を実装することができます。
この方法は、既存の実装コードとの整合性を保ちながら非同期機能を拡張する際に有効です。
修正後のコード例の確認
状態機械による非同期処理の管理
非同期メソッドが正常に動作するためには、コンパイラがメソッド内に存在する await
演算子を検出し、状態機械を生成する必要があります。
以下のサンプルコードでは、async
修飾子と await
演算子を利用して、非同期処理が状態機械によってどのように管理されるかを確認することができます。
using System;
using System.Threading.Tasks;
class Program
{
// 主に非同期処理を実行するMain関数
static async Task Main(string[] args)
{
// 状態機械が生成され、LongRunningOperationのawait時に一時停止・再開が行われる
string message = await LongRunningOperation();
Console.WriteLine("処理完了: " + message);
}
// 非同期処理サンプル
static async Task<string> LongRunningOperation()
{
// 状態機械によりTask.Delay中に制御が戻される
await Task.Delay(1000);
// 文字列を返却
return "結果取得";
}
}
処理完了: 結果取得
await演算子の役割と使用ポイント
await
演算子は、非同期操作が完了するのを待機するために使用されます。
この演算子を用いると、実行中のメソッドが一時的に中断され、指定されたタスクが完了した後に中断箇所から再開されます。
また、await
は、例外処理の伝播も適切に管理してくれるため、エラーハンドリングが簡単になる利点があります。
以下の例では、await
演算子を利用したシンプルな非同期処理を確認することができます。
using System;
using System.Threading.Tasks;
class Program
{
// 非同期メイン関数。await演算子により、Task.Delayの完了を待機する
static async Task Main(string[] args)
{
Console.WriteLine("非同期処理開始");
await PerformAsyncOperation();
Console.WriteLine("非同期処理終了");
}
// 非同期操作。await演算子を使用してタスク完了を待機
static async Task PerformAsyncOperation()
{
// 疑似的な非同期処理(1秒待機)
await Task.Delay(1000);
Console.WriteLine("Task.Delay 完了");
}
}
非同期処理開始
Task.Delay 完了
非同期処理終了
まとめ
この記事では、C#におけるasync
修飾子の役割とその基本動作、及び非具象メソッドに適用する際の制約について解説しました。
特に、インターフェイス内での利用例を通して、本体の実装がない場合に発生するCS1994エラーの原因と、その修正方法(async
修飾子の削除や既定具象メソッドの利用)の具体例を示しています。
また、状態機械やawait
演算子の役割も確認できる内容となっています。