CS0~400

C# コンパイラエラー CS0070 の原因と対処方法について解説

CS0070 は、C#で発生するコンパイラエラーです。

イベントは、外部からは「+=」や「-=」を使って操作する必要があり、直接代入することはできません。

コード内でイベントに直接アクセスしようとすると、このエラーが検出されます。

CS0070 エラーの背景

C# のイベントは、オブジェクト間で通知を伝達するための仕組みとして利用されます。

イベントは基本的に、メソッドの登録や解除によって機能し、特定の操作が実行されたタイミングで関連する処理が呼び出されます。

ここでは、C# のイベントの基本的な仕組みと、CS0070 エラーが発生する背景について解説します。

C# のイベントの基本的な仕組み

イベントの定義と利用制限

イベントは、クラス内で宣言され、外部のクラスからは += または -= を使用してイベントハンドラを登録または解除します。

宣言例として、以下のコードが挙げられます。

using System;
// 日本語のコメント:イベントハンドラのデリゲート宣言
public delegate void EventHandler(string message);
public class CustomEventClass
{
    // 日本語のコメント:イベントの宣言。外部からは += および -= による操作のみ許可されている。
    public event EventHandler OnEvent;
    public void TriggerEvent()
    {
        // 日本語のコメント:イベント発火。OnEvent に登録されたハンドラを実行する。
        OnEvent?.Invoke("イベントが発生しました。");
    }
}
public class Program
{
    public static void Main()
    {
        CustomEventClass instance = new CustomEventClass();
        // 日本語のコメント:イベントハンドラを登録
        instance.OnEvent += (msg) => Console.WriteLine(msg);
        // 日本語のコメント:イベントを発火
        instance.TriggerEvent();
    }
}

この例は、イベントが定義されるとき、外部からは直接イベントに値を代入することができず、必ず += または -= を使用する必要があることを示しています。

すなわち、イベントは外部のアクセスに対して利用制限がかかっています。

「+=」と「-=」の正しい使い方

イベントの利用において、+= はイベントハンドラ(メソッド)の参照を追加するために利用され、-= は既に追加されたイベントハンドラを解除するために利用されます。

以下に正しい使い方の例を示します。

using System;
public delegate void EventHandler(string info);
public class EventDemo
{
    public event EventHandler OnAction;
    public void RaiseAction(string info)
    {
        OnAction?.Invoke(info);
    }
    public static void Main()
    {
        EventDemo demo = new EventDemo();
        // イベントハンドラの登録
        demo.OnAction += (info) => Console.WriteLine($"登録ハンドラ:{info}");
        demo.RaiseAction("ハンドラ登録後の実行");
        // イベントハンドラの解除
        demo.OnAction -= (info) => Console.WriteLine($"登録ハンドラ:{info}");
        demo.RaiseAction("ハンドラ解除後の実行");
    }
}

上記のコードでは、+= によって匿名メソッドをイベントハンドラとして登録しており、-= によってイベントハンドラを解除しようとしています。

ただし、匿名メソッドの場合、解除するときは同一のインスタンスが必要となるので、実際に利用する際はハンドラに変数を割り当てると良いです。

エラー発生の原因

イベントへの直接代入の禁止

CS0070 エラーは、イベントに対して直接代入しようとした場合に発生します。

具体的には、クラス外部から = 演算子を使用してイベントの値を設定することは禁止されています。

C# のコンパイラは、イベント操作を += および -= のみ許可しており、直接代入を行おうとするとエラー CS0070 が発生する設計となっています。

たとえば、以下のようなコードがエラーとなります。

using System;
public delegate void EventHandler(string param);
public class SampleClass
{
    public event EventHandler OnSampleEvent;
}
public class InvalidUsage
{
    public static void Main()
    {
        SampleClass sample = new SampleClass();
        // 日本語のコメント:直接代入を試みるため CS0070 エラーが発生する
        // sample.OnSampleEvent = new EventHandler((param) => Console.WriteLine(param));
    }
}

外部からのアクセス制限

イベントは、クラスの内部では自由に利用できるものの、クラス外部からは参照の追加や削除のみが許可されています。

これにより、意図しないイベントハンドラの差し替えや、イベントの無効化を防止することができます。

たとえば、クラス外部でイベントの中身全体を上書きすることはできず、必ず登録されたハンドラに対して操作を行う必要があります。

コード例によるエラー解析

イベントに関する CS0070 エラーの原因を理解するため、具体的なコード例で発生するケースと、その挙動について解説します。

発生するケースの解説

誤ったコード例の動作

以下のコード例は、クラス外部からイベントに対して直接代入しようとするため、コンパイル時に CS0070 エラーが発生します。

コメント内に日本語で説明を記載しているため、エラーの原因が分かりやすくなっています。

using System;
public delegate void EventHandler(string message);
public class A
{
    // 日本語のコメント:イベントの宣言。外部からの直接代入は禁止される。
    public event EventHandler Click;
    public static void Main()
    {
        A instance = new A();
        // 日本語のコメント:直接代入によりコンパイルエラー CS0070 が発生する
        // EventHandler handler = (msg) => Console.WriteLine("クリックイベント発生:" + msg);
        // instance.Click = handler;  // CS0070 エラー
        // 正しい操作は、次のように += を使用することです。
        instance.Click += (msg) => Console.WriteLine("クリックイベント発生:" + msg);
        instance.Click?.Invoke("サンプルメッセージ");
    }
}

この例では、直接代入をコメントアウトして示していますが、実際に代入を試みるとコンパイルエラーが発生します。

コンパイラは、イベントに対して = 演算子を使用する操作を禁じています。

正しいコード例との比較

正しいコード例では、+= を利用することでイベントハンドラの追加が正しく行われ、エラーが回避されます。

以下の例を参考にしてください。

using System;
public delegate void EventHandler(string message);
public class A
{
    // 日本語のコメント:イベントの宣言
    public event EventHandler Click;
    public void TriggerClick()
    {
        // 日本語のコメント:イベントの発火
        Click?.Invoke("クリック発火");
    }
    public static void Main()
    {
        A instance = new A();
        // 日本語のコメント:イベントハンドラを += 演算子で追加する
        instance.Click += (message) => Console.WriteLine(message);
        instance.TriggerClick();
    }
}

この例では、+= を使用してイベントハンドラを登録しているため、CS0070 エラーは発生しません。

また、イベントの発火に伴い登録ハンドラが正しく動作する様子が確認できます。

CS0070 エラーの対処方法

CS0070 エラーの対処は、イベントへのアクセス方法を適正に行うことによって実現します。

ここでは、正しいイベント操作の記述方法と、コーディング上の確認ポイントについて説明します。

正しいイベント操作の記述方法

参照追加(+=)の利用方法

イベントハンドラの追加は、必ず += 演算子を使用します。

イベントに対してハンドラを追加する際は、以下の例のように記述してください。

using System;
public delegate void EventHandler(string info);
public class EventOperator
{
    public event EventHandler OnOperate;
    public void Operate(string info)
    {
        OnOperate?.Invoke(info);
    }
    public static void Main()
    {
        EventOperator oper = new EventOperator();
        // 日本語のコメント:イベントハンドラを += を使用して登録する
        EventHandler handler = (info) => Console.WriteLine("処理内容:" + info);
        oper.OnOperate += handler;
        oper.Operate("参照追加の動作確認");
    }
}

上記のコードでは、ハンドラを変数に代入してから += を使用して登録しています。

これにより、登録したハンドラを後で正しく解除することも容易になります。

参照削除(-=)の利用方法

逆に、登録済みのイベントハンドラを解除する場合は、-= 演算子を使用する必要があります。

以下のサンプルコードは、解除の手順を示しています。

using System;
public delegate void EventHandler(string info);
public class EventOperator
{
    public event EventHandler OnOperate;
    public void Operate(string info)
    {
        OnOperate?.Invoke(info);
    }
    public static void Main()
    {
        EventOperator oper = new EventOperator();
        // 日本語のコメント:イベントハンドラ登録の例
        EventHandler handler = (info) => Console.WriteLine("処理内容:" + info);
        oper.OnOperate += handler;
        // 日本語のコメント:イベントを発火。ハンドラが実行される。
        oper.Operate("イベントハンドラ登録後");
        // 日本語のコメント:イベントハンドラを解除する
        oper.OnOperate -= handler;
        // 日本語のコメント:解除後はハンドラが実行されない
        oper.Operate("イベントハンドラ解除後");
    }
}

このコードでは、+= でハンドラを追加後、-= によって解除している様子が示されています。

解除後にイベントを発火しても、ハンドラが呼び出されない点を確認できます。

コーディング上の確認ポイント

イベントハンドラの適切な設計

イベントハンドラは、イベントを受信するための一連のメソッドや匿名メソッドとして設計します。

ここでは、設計時に気を付けるべきポイントを以下に示します。

  • イベントハンドラを登録する際、複数のハンドラが登録される可能性があるため、イベント発火時にすべてのハンドラが呼び出されることを意識してください。
  • 同じ匿名メソッドを複数回 += で登録すると、解除時に同一のインスタンスを指定できない可能性があるため、必要に応じてハンドラを変数に保持すると良いです。
  • イベントハンドラはシンプルな処理に留め、複雑なロジックの場合は、別メソッドに分割して記述することで可読性が向上します。

たとえば、次のコードは、イベントハンドラの設計上の注意点を反映しています。

using System;
public delegate void EventHandler(string detail);
public class DesignDemo
{
    public event EventHandler OnAction;
    // 日本語のコメント:複数のハンドラが登録されても問題なく動作するように設計する
    public void ExecuteAction(string detail)
    {
        OnAction?.Invoke(detail);
    }
    // 日本語のコメント:イベントハンドラとして別メソッドを利用
    public void LogAction(string detail)
    {
        Console.WriteLine("ログ:" + detail);
    }
    public static void Main()
    {
        DesignDemo demo = new DesignDemo();
        // 日本語のコメント:イベントハンドラを変数に保持して登録する
        EventHandler handler = demo.LogAction;
        demo.OnAction += handler;
        // 日本語のコメント:匿名メソッドも追加可能
        demo.OnAction += (detail) => Console.WriteLine("匿名ハンドラ:" + detail);
        demo.ExecuteAction("設計上の確認ポイントの動作確認");
        // 日本語のコメント:不要になったらハンドラを解除する
        demo.OnAction -= handler;
        demo.ExecuteAction("一部ハンドラ解除後の動作確認");
    }
}

この例では、複数のハンドラが同時に登録される状況と、ハンドラが解除された後の動作を示すことで、正しいイベント設計のポイントを説明しています。

まとめ

この記事では、C# のイベントの基本的な仕組みとその利用制限について説明しています。

イベントの定義、直接代入が禁止される理由、外部からの参照追加(+=)と参照削除(-=)のみが許される設計原理を理解できる内容です。

さらに、誤ったコード例と正しいコード例を比較しながら、CS0070 エラーの発生原因と対処方法を具体的に示しており、正しいイベントハンドラ設計のポイントも把握できるようになっています。

関連記事

Back to top button
目次へ