C#コンパイラエラー CS0065 について解説:イベントアクセサーの正しい実装方法
CS0065エラーは、C#でイベントを定義する際に、必ずaddアクセサーとremoveアクセサーの両方を実装する必要があることを示しています。
イベントプロパティでこれらのアクセサーの一方または両方が実装されていない場合に発生するため、エラー解消には両アクセサーの実装を追加してください。
CS0065エラー発生の背景
イベントプロパティの基本仕様
C#では、イベントはデリゲートのラッパーとして実装され、クラス外から発行や解除を行うことができます。
通常、イベントはフィールドとして宣言されると、コンパイラが自動でadd
アクセサーとremove
アクセサーを生成します。
しかし、イベントプロパティとして実装する場合は、両方のアクセサーを明示的に定義する必要があります。
イベントプロパティを使用する際は、以下の点に注意します。
- インスタンスの外部からイベントに委譲先を追加・削除するための仕組みを提供
- イベントのカプセル化を実現し、内部ロジックをコントロール可能にする
addアクセサーとremoveアクセサーの役割
add
アクセサーは、イベントに委譲先(ハンドラー)を追加するために呼ばれるメソッドです。
一方、remove
アクセサーは追加された委譲先を解除するために使用されます。
実装例として、以下のようにadd
とremove
で内部リストにイベントハンドラーを管理する仕組みを設けることが考えられます。
add
アクセサー:新たなハンドラーをリストに追加remove
アクセサー:既存のハンドラーをリストから削除
フィールドと非フィールドのイベントの違い
フィールド形式のイベントでは、コンパイラがadd
およびremove
の実装を自動的に生成します。
そのため、ユーザはイベントの追加や削除のロジックを明示的に記述する必要がありません。
しかし、非フィールド形式、すなわちイベントプロパティとして実装する場合、必ず両方のアクセサーを実装しなければなりません。
その理由は以下の通りです。
- フィールド:シンプルなイベント操作を自動管理
- 非フィールド:イベントの発行タイミングやロジックをカスタマイズ可能な代わりに、両方のアクセサーが必要
エラーの具体例と原因分析
コンパイラエラーメッセージの解説
エラー CS0065は、イベントプロパティを定義する際にadd
アクセサーとremove
アクセサーの両方が実装されていない場合に発生します。
メッセージは以下のように表示されます。
'event': イベント プロパティには、add および remove アクセサーの両方を指定する必要があります
このエラーは、開発者がイベントプロパティの実装を途中で終わらせた場合や、どちらか一方を省略した場合に発生します。
問題のあるコード例
イベントプロパティを宣言したものの、add
とremove
アクセサーの実装が欠如している例が以下のコードです。
アクセサー実装の欠如によるエラー発生
// CS0065_ErrorExample.cs
using System;
// カスタムイベントハンドラーのデリゲートを定義
public delegate void EventHandler(object sender, int e);
public class MyClass
{
// イベントプロパティの宣言(アクセサー未実装)
public event EventHandler Click // CS0065エラーが発生する部分
{
// addとremoveアクセサーの実装がコメントアウトされた状態
/*
add
{
// ハンドラーの追加処理(実装例では省略)
}
remove
{
// ハンドラーの削除処理(実装例では省略)
}
*/
}
public static void Main()
{
// エントリポイント(コンパイルはこの部分で発生するエラーに気づく)
}
}
このコードでは、Click
イベントのadd
とremove
アクセサーが実装されていないため、コンパイラがエラーを出力します。
正しい実装方法の解説
addアクセサーの実装例
add
アクセサーでは、イベントハンドラーを内部のデリゲート変数に追加する処理を実装します。
以下のサンプルコードでは、内部に_clickHandlers
という変数を用いてイベントハンドラーの管理を行っています。
using System;
// カスタムイベントハンドラーのデリゲートを定義(日本語コメント付き)
public delegate void EventHandler(object sender, int e);
public class MyClass
{
// 内部でイベントハンドラーを保持する変数
private EventHandler _clickHandlers;
// イベントプロパティの実装(addアクセサー)
public event EventHandler Click
{
add
{
// 新たなハンドラーを_internalハンドラーに追加
_clickHandlers += value;
// 簡単な出力で処理を確認可能
System.Console.WriteLine("ハンドラーが追加されました");
}
remove
{
// 後の解説にて詳細説明
_clickHandlers -= value;
System.Console.WriteLine("ハンドラーが削除されました");
}
}
public static void Main()
{
MyClass instance = new MyClass();
// 匿名メソッドを使ってイベントハンドラーを追加
instance.Click += (sender, e) =>
{
System.Console.WriteLine("イベントが発生しました");
};
// 下記はイベントを発火させるための模擬処理
instance.TriggerClick();
}
// 内部用にイベント発火用のメソッドを実装
public void TriggerClick()
{
_clickHandlers?.Invoke(this, 100); // 仮のパラメータ100を渡す
}
}
ハンドラーが追加されました
イベントが発生しました
removeアクセサーの実装例
remove
アクセサーは、イベントハンドラーが正しく解除されるよう実装します。
上記サンプルコード内ですでに、remove
アクセサーは内包する変数から対象のハンドラーを削除する形で実装しています。
以下はその抜粋です。
remove
{
// ハンドラーを削除して、内部変数を更新
_clickHandlers -= value;
System.Console.WriteLine("ハンドラーが削除されました");
}
正しいコード例の詳細解説
正しい実装では、イベントプロパティに対してadd
とremove
の両方で適切な処理が記述されています。
以下のポイントに注意してください。
- 内部変数(ここでは
_clickHandlers
)を用いて、複数のハンドラーの追加と削除がシームレスに処理可能 add
アクセサーでは、追加処理後にデバッグ用の出力を行い、処理の流れを確認できるremove
アクセサーでは、解除処理後に出力でその動作を確認Main
関数をエントリーポイントとして、実際にイベントの追加と発火の挙動をテストしている
この正しいコード例では、イベントが正常に発火し、ハンドラーの追加および削除が期待通りに動作することが確認できます。
イベントプロパティを使って、柔軟なイベント管理の仕組みを実装するための基本が理解できる内容となっています。
修正後の確認手順
コンパイル確認の方法
修正後は、コード全体がコンパイルエラーを出さずにビルドできるか確認する必要があります。
- 開発環境でソリューションをビルドし、エラーが解消されたことを確認してください。
- コンパイラからの警告やエラーメッセージが出ない場合、正しい実装が行われたと考えられます。
動作検証のポイント
コンパイルが通った後は、実際にイベントの動作を確認します。
以下のポイントに注目してください。
- ハンドラー追加時に、
add
アクセサー内の出力がコンソールに表示されること - イベント発火後に、追加されたハンドラーが呼び出され、対応する出力が確認できること
remove
アクセサーを利用してハンドラーを解除し、その後イベントを発火した際に、解除されたハンドラーからの出力が発生しないこと
上記の動作検証を行うことで、イベントプロパティが意図した通りに動作していることを確認することができます。
まとめ
この記事では、イベントプロパティにおけるエラー CS0065 の原因と解決方法が理解できます。
イベントプロパティを利用する際、コンパイラが自動生成するフィールドイベントと異なり、明示的にadd
およびremove
アクセサーを実装する必要がある点を解説しました。
具体例を通じて不備のあるコードと修正後の正しい実装方法、さらにはコンパイルおよび動作検証の手順について説明しており、イベントの管理方法が身につく内容です。