スポンサーリンク

C# eventのオーバーライドと基底クラスで発生するイベントの処理

C#のeventはvirtualが宣言できて、派生クラスでoverrideできますが、そのオーバーライドされたイベントに登録したイベントハンドラを基底クラス側から呼び出せません。これはeventは宣言された同一クラス内でのみ発生させることができると言うC#の言語仕様なので当たり前と言えば当たり前の動作なのですが、適切な処理をしていないがために基底クラス側でイベントを発生させたときにExceptionが発生してしまったりと言うことがあります。(別の言い方を知れば派生クラスから基底クラスのイベントを発生させることができません。)

まぁ頭の悪い子(俺)はそれでずっとうじうじ悩んだりするわけです。

具体的にはイベントをオーバライドしたら、そのオーバーライドしたイベントの基底クラスのイベントのイベントハンドラを用意し、そのイベントハンドラで、オーバーライドしたイベントを呼び出します。ようはイベントが伝達できるようにすればいいわけですねって、オーバーライドって文字がたくさん出てきて何かよくわかりませんね。と言うことでサンプルを以下にのせます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> System.Linq;
<span class="hljs-keyword">using</span> System.Text;
<span class="hljs-keyword">namespace</span> <span class="hljs-title">EventTest</span>
{
<span class="hljs-keyword">class</span> <span class="hljs-title">Program</span>
{
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)</span> {
Test test = <span class="hljs-keyword">new</span> Test();
test.TestEvent += <span class="hljs-keyword">new</span> EventHandler<testeventargs>(test_TestEvent);
test.Fire();
test.BaseFire();
Console.Read();
}
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test_TestEvent</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> sender, TestEventArgs e</span>)</span> {
Console.WriteLine(e.Message);
}
}
<span class="hljs-comment">//基底クラス</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">TestBase</span>
{
<span class="hljs-comment">//イベントをvirtualで宣言する。</span>
<span class="hljs-keyword">internal</span> <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">event</span> EventHandler<testeventargs> TestEvent;
<span class="hljs-comment">//イベントを発生させる</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">BaseFire</span>(<span class="hljs-params"></span>)</span> {
TestEventArgs args = <span class="hljs-keyword">new</span> TestEventArgs(<span class="hljs-string">"Fired on Base class"</span>);
TestEvent(<span class="hljs-keyword">this</span>, args);<span class="hljs-comment">//派生クラス側で処理がないとイベントを発生できないので、ここでExceptionが発生する。</span>
}
}
<span class="hljs-comment">//派生クラス</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Test</span> : <span class="hljs-title">TestBase</span>
{
<span class="hljs-keyword">internal</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">event</span> EventHandler<testeventargs>TestEvent;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Test</span>(<span class="hljs-params"></span>)</span> {
<span class="hljs-comment">//基底クラスのイベントにイベント伝達用のイベントハンドラを登録する。</span>
<span class="hljs-keyword">base</span>.TestEvent += <span class="hljs-keyword">new</span> EventHandler<testeventargs>(Test_TestEvent);
}
<span class="hljs-comment">//イベント伝達用のイベントハンドラ</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Test_TestEvent</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> sender, TestEventArgs e</span>)</span> {
<span class="hljs-comment">//利用先に基底クラスのイベントを伝達する</span>
<span class="hljs-keyword">this</span>.TestEvent(sender, e);
}
<span class="hljs-comment">//イベントを発生させる</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Fire</span>(<span class="hljs-params"></span>)</span> {
TestEvent(<span class="hljs-keyword">this</span>, <span class="hljs-keyword">new</span> TestEventArgs(<span class="hljs-string">"Fired on Extended class"</span>));
}
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">TestEventArgs</span> : <span class="hljs-title">EventArgs</span>
{
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">TestEventArgs</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> message</span>)</span> {
<span class="hljs-keyword">this</span>.Message = message;
}
<span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Message { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }
}
}</testeventargs></testeventargs></testeventargs></testeventargs>
<span class="hljs-keyword">using</span> System; <span class="hljs-keyword">using</span> System.Collections.Generic; <span class="hljs-keyword">using</span> System.Linq; <span class="hljs-keyword">using</span> System.Text; <span class="hljs-keyword">namespace</span> <span class="hljs-title">EventTest</span> { <span class="hljs-keyword">class</span> <span class="hljs-title">Program</span> { <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Main</span>(<span class="hljs-params"><span class="hljs-keyword">string</span>[] args</span>)</span> { Test test = <span class="hljs-keyword">new</span> Test(); test.TestEvent += <span class="hljs-keyword">new</span> EventHandler<testeventargs>(test_TestEvent); test.Fire(); test.BaseFire(); Console.Read(); } <span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test_TestEvent</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> sender, TestEventArgs e</span>)</span> { Console.WriteLine(e.Message); } } <span class="hljs-comment">//基底クラス</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">TestBase</span> { <span class="hljs-comment">//イベントをvirtualで宣言する。</span> <span class="hljs-keyword">internal</span> <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">event</span> EventHandler<testeventargs> TestEvent; <span class="hljs-comment">//イベントを発生させる</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">BaseFire</span>(<span class="hljs-params"></span>)</span> { TestEventArgs args = <span class="hljs-keyword">new</span> TestEventArgs(<span class="hljs-string">"Fired on Base class"</span>); TestEvent(<span class="hljs-keyword">this</span>, args);<span class="hljs-comment">//派生クラス側で処理がないとイベントを発生できないので、ここでExceptionが発生する。</span> } } <span class="hljs-comment">//派生クラス</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Test</span> : <span class="hljs-title">TestBase</span> { <span class="hljs-keyword">internal</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">event</span> EventHandler<testeventargs>TestEvent; <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Test</span>(<span class="hljs-params"></span>)</span> { <span class="hljs-comment">//基底クラスのイベントにイベント伝達用のイベントハンドラを登録する。</span> <span class="hljs-keyword">base</span>.TestEvent += <span class="hljs-keyword">new</span> EventHandler<testeventargs>(Test_TestEvent); } <span class="hljs-comment">//イベント伝達用のイベントハンドラ</span> <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">Test_TestEvent</span>(<span class="hljs-params"><span class="hljs-keyword">object</span> sender, TestEventArgs e</span>)</span> { <span class="hljs-comment">//利用先に基底クラスのイベントを伝達する</span> <span class="hljs-keyword">this</span>.TestEvent(sender, e); } <span class="hljs-comment">//イベントを発生させる</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Fire</span>(<span class="hljs-params"></span>)</span> { TestEvent(<span class="hljs-keyword">this</span>, <span class="hljs-keyword">new</span> TestEventArgs(<span class="hljs-string">"Fired on Extended class"</span>)); } } <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">TestEventArgs</span> : <span class="hljs-title">EventArgs</span> { <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">TestEventArgs</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> message</span>)</span> { <span class="hljs-keyword">this</span>.Message = message; } <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Message { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } } }</testeventargs></testeventargs></testeventargs></testeventargs>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EventTest
{
    class Program
    {
        static void Main(string[] args) {
            Test test = new Test();
            test.TestEvent += new EventHandler(test_TestEvent);
            test.Fire();
            test.BaseFire();
            Console.Read();
        }

        static void test_TestEvent(object sender, TestEventArgs e) {
            Console.WriteLine(e.Message);
        }
    }

    //基底クラス
    public class TestBase
    {
        //イベントをvirtualで宣言する。
        internal virtual event EventHandler TestEvent;
        //イベントを発生させる
        public void BaseFire() {
            TestEventArgs args = new TestEventArgs("Fired on Base class");
            TestEvent(this, args);//派生クラス側で処理がないとイベントを発生できないので、ここでExceptionが発生する。
        }

    }

    //派生クラス
    public class Test : TestBase
    {
        internal override event EventHandlerTestEvent;

        public Test() {
            //基底クラスのイベントにイベント伝達用のイベントハンドラを登録する。
            base.TestEvent += new EventHandler(Test_TestEvent);
        }
        //イベント伝達用のイベントハンドラ
        void Test_TestEvent(object sender, TestEventArgs e) {
            //利用先に基底クラスのイベントを伝達する
            this.TestEvent(sender, e);
        }
        //イベントを発生させる
        public void Fire() {
            TestEvent(this, new TestEventArgs("Fired on Extended class"));
        }
    }

    public class TestEventArgs : EventArgs
    {
        public TestEventArgs(string message) {
            this.Message = message;
        }
        public string Message { get; set; }
    }
}

実行結果

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Fired <span class="hljs-keyword">on</span> Extended <span class="hljs-built_in">class</span>
Fired <span class="hljs-keyword">on</span> Base <span class="hljs-built_in">class</span>
Fired <span class="hljs-keyword">on</span> Extended <span class="hljs-built_in">class</span> Fired <span class="hljs-keyword">on</span> Base <span class="hljs-built_in">class</span>
Fired on Extended class
Fired on Base class

当たり前と言えば当たり前のコードなんですがeventのvirtual / override含めて書籍やネットにあまりサンプルが無いようなので載せてみました。

コメント

タイトルとURLをコピーしました