講義メモ

・テキスト編「第12章 デリゲートとイベント」から
・ゲーム開発演習:文字列の描画(つづき)、画面遷移、など

第12章 デリゲートとイベント

p.295 デリゲートとは

・メソッドへの参照を保持しておいて、これを用いてメソッドを呼び出せる仕掛け
・つまり、デリゲートを通じてのメソッドの呼び出しが可能
・C/C++における「関数へのポインタ」と同じ考え方を洗練したもの
・イベント処理などで必須のテクニック
・宣言書式: delegate メソッドの戻り値型 デリゲート名(メソッドの引数リスト);
・例:bool answer(int x){…}メソッド用のデリゲートmdならば、delegate bool md(int w) とする
・利用にはデリゲートオブジェクトの生成が必要で、この時に呼び出したいメソッドの指定もできる
・生成書式: デリゲート名 参照変数 = new デリゲート名(メソッド名);
・例:bool answer(int x){…}メソッド用のデリゲートmdならば、md mymd = new md(answer);
・デリゲート経由で指定済のメソッドを呼び出すには、参照変数を別名のように利用できる
・例: bool b = mymd(5); // answer(5)が呼ばれ戻り値が返される

p.297 delegate01.cs について

・Main()メソッドの戻り値をint型にしているが、この場合、必要性はないので、いつもの通り voidにして良い
・よって「return 0;」も不要

p.297 delegate01.cs(修正後)

//p.297 delegate01.cs
using System;
delegate void MyDelegate();
class delegate01 {
    public static void show() { //Main()メソッドから呼ばれる静的メソッド
        Console.WriteLine("呼ばれました");
    }
    public static void Main() {
        //直接showメソッドを呼び出す
        show();
        //デリゲートの作成
        MyDelegate md = new MyDelegate(show);
        //デリゲートを通してshowメソッドを実行
        md();
    }
}

アレンジ演習:p.297 delegate01.cs

・上記の例のメソッドを追記し、これを呼び出すデリゲートをMainに追加して動作を確認しよう
 static bool answer(int x){ return (x >= 0); } //xが0以上ならtrueを、でなければfalseを返す

作成例

//アレンジ演習:p.297 delegate01.cs
using System;
delegate void MyDelegate();
delegate bool MyDelegate2(int i); //【追加】answer用のデリゲートの宣言
class delegate01 {
    public static void show() { //Main()メソッドから呼ばれる静的メソッド
        Console.WriteLine("呼ばれました");
    }
    static bool answer(int x){ //【以下追加】※publicでなくて良い
        return (x >= 0); //xが0以上ならtrueを、でなければfalseを返す
    }
    public static void Main() {
        show(); //直接showメソッドを呼び出す
        MyDelegate md = new MyDelegate(show);//デリゲートの作成
        md();//デリゲートを通してshowメソッドを実行
        MyDelegate2 md2 = new MyDelegate2(answer); //【追加】answer用のデリゲートの作成
        Console.WriteLine(md2(10));//【追加】デリゲートを通して引数10を渡して実行結果を表示
    }
}

p.298 デリゲートとは:別のクラスに属するメソッドをデリゲートを通して用いる

・デリゲートの宣言は特に変わらない
・別のクラスに属するインスタンスメソッドであれば、インスタンスを生成して、参照変数.メソッド名で、デリゲートを作成すれば良い

p.297 delegate02.cs

//p.298 delegate02.cs
using System;
delegate void MyDelegate(); //デリゲートの宣言
class MyClass
{
    public void show()
    {
        Console.WriteLine("呼ばれました");
    }
}
class delegate02
{
    public static void Main()
    {
        MyClass mc = new MyClass(); //呼び出すメソッドのあるクラスのインスタンスを生成
        mc.show();
        MyDelegate m = new MyDelegate(mc.show); //参照変数.メソッド名を与える
        m(); //デリゲートを通してメソッドを実行
    }
}

アレンジ演習:p.297 delegate02.cs

・MyClassクラスに下記のメソッドを追記し、これを呼び出すデリゲートをMainに追加して動作を確認しよう
 bool answer(int x){ return (x >= 0); } //xが0以上ならtrueを、でなければfalseを返す

作成例

//アレンジ演習:p.298 delegate02.cs
using System;
delegate void MyDelegate(); //デリゲートの宣言
delegate bool MyDelegate2(int i); //【追加】answer用のデリゲートの宣言
class MyClass {
    public void show() {
        Console.WriteLine("呼ばれました");
    }
    public bool answer(int x) { //【以下追加】※publicが必要
        return (x >= 0); //xが0以上ならtrueを、でなければfalseを返す
    }
}
class delegate02 {
    public static void Main() {
        MyClass mc = new MyClass(); //呼び出すメソッドのあるクラスのインスタンスを生成
        mc.show();
        MyDelegate m = new MyDelegate(mc.show); //参照変数.メソッド名を与える
        m(); //デリゲートを通してメソッドを実行
        MyDelegate2 m2 = new MyDelegate2(mc.answer); //【追加】answer用のデリゲートの作成
        Console.WriteLine(m2(10));//【追加】デリゲートを通して引数10を渡して実行結果を表示
    }
}

p.298 デリゲートとは:別のクラスに属する静的メソッドをデリゲートを通して用いる

・デリゲートの宣言は特に変わらない
・別のクラスに属する静的メソッドであれば、クラス名.メソッド名で、デリゲートを作成すれば良い

アレンジ演習:p.299 delegate03.cs

・MyClassクラスに下記のメソッドを追記し、これを呼び出すデリゲートをMainに追加して動作を確認しよう
 public static bool answer(int x){ return (x >= 0); } //xが0以上ならtrueを、でなければfalseを返す

作成例

//アレンジ演習:p.299 delegate03.cs
using System;
delegate string MyDelegate(string a, string b); //デリゲートの宣言(戻り値有り、2引数)
delegate bool MyDelegate2(int i); //【追加】answer用のデリゲートの宣言(戻り値有り、1引数)
class MyClass {
    public static string show(string s1, string s2) { //静的メソッド
        return s1 + "は" + s2 + "です";
    }
    public static bool answer(int x){ //【以下追加】静的メソッド
        return (x >= 0); //xが0以上ならtrueを、でなければfalseを返す
    }
}
class delegate03 {
    public static void Main() {
        Console.WriteLine(MyClass.show("猫", "ほ乳類"));
        MyDelegate md = new MyDelegate(MyClass.show); //静的メソッドなのでクラス名.メソッド名
        Console.WriteLine(md("C#", "おもしろい"));
        MyDelegate2 m2 = new MyDelegate2(MyClass.answer); //【追加】answer用のデリゲートの作成
        Console.WriteLine(m2(10));//【追加】デリゲートを通して引数10を渡して実行結果を表示
    }
}

p.298 デリゲートとは:デリゲート変数の再利用

・戻り値型と引数リストが同じメソッドを呼び出すデリゲートであれば、デリゲート変数を再利用できる
・これにより、同じ名前のデリゲートを通して異なるメソッドを呼び出すことができる

p.300 delegate04.cs

//p.300 delegate04.cs
using System;
delegate DateTime MyDelegate(DateTime dt, int n); //デリゲートの宣言(戻り値有り、2引数)
class MyClass1 { //デリゲートに用いるメソッドのあるクラス①
    public DateTime Calc(DateTime d, int n) { //デリゲートに用いるインスタンスメソッド①
        return d.AddDays(n); //日付時刻オブジェクトのn日後の日付時刻オブジェクトを返す
    }
}
class Myclass2 { //デリゲートに用いるメソッドのあるクラス②
    public DateTime Calc(DateTime d, int n) { //デリゲートに用いるインスタンスメソッド②
        return d.AddHours(n); //日付時刻オブジェクトのn時間後の日付時刻オブジェクトを返す
    }
}
class delegate04 {
    public static void Main() {
        MyClass1 mc1 = new MyClass1(); //デリゲートに用いるメソッドのあるクラス①
        Myclass2 mc2 = new Myclass2(); //デリゲートに用いるメソッドのあるクラス②
        MyDelegate md = new MyDelegate(mc1.Calc); //デリゲートに用いるインスタンスメソッド①を指定
        DateTime dt = DateTime.Now; //現在日付時刻のオブジェクトを得る
        DateTime mydate; //作業用の日付時刻オブジェクトを得る
        mydate = md(dt, 100); //デリゲート経由でMyClass1のCalcメソッドを呼び、現在日付時刻オブジェクトと100を渡す
        Console.WriteLine("今日から100日後は、{0}です", mydate.ToShortDateString());
        md = new MyDelegate(mc2.Calc); //デリゲートに用いるインスタンスメソッド②で置き換える
        mydate = md(dt, 100); //デリゲート経由でMyClass2のCalcメソッドを呼び、現在日付時刻オブジェクトと100を渡す
        Console.WriteLine("今から100時間後は、{0}です", mydate);
    }
}

アレンジ演習:p.299 delegate03.cs

・MyClass2クラスに下記のメソッドを追記し、名前が異なるメソッドも同じデリゲートで呼び出せることを確認しよう
 public DateTime Calc2(DateTime d, int n) { //デリゲートに用いるインスタンスメソッド③
  return d.AddDays(n).AddHours(n); //n日後のn時間後の日付時刻オブジェクトを返す
 }

作成例

//アレンジ演習:p.300 delegate04.cs
using System;
delegate DateTime MyDelegate(DateTime dt, int n); //デリゲートの宣言(戻り値有り、2引数)
class MyClass1 { //デリゲートに用いるメソッドのあるクラス①
    public DateTime Calc(DateTime d, int n) { //デリゲートに用いるインスタンスメソッド①
        return d.AddDays(n); //日付時刻オブジェクトのn日後の日付時刻オブジェクトを返す
    }
}
class Myclass2 { //デリゲートに用いるメソッドのあるクラス②
    public DateTime Calc(DateTime d, int n) { //デリゲートに用いるインスタンスメソッド②
        return d.AddHours(n); //日付時刻オブジェクトのn時間後の日付時刻オブジェクトを返す
    }
    public DateTime Calc2(DateTime d, int n) { //【以下追加】デリゲートに用いるインスタンスメソッド③
        return d.AddDays(n).AddHours(n); //n日後のn時間後の日付時刻オブジェクトを返す
    }
}
class delegate04 {
    public static void Main() {
        MyClass1 mc1 = new MyClass1(); //デリゲートに用いるメソッドのあるクラス①
        Myclass2 mc2 = new Myclass2(); //デリゲートに用いるメソッドのあるクラス②
        MyDelegate md = new MyDelegate(mc1.Calc); //デリゲートに用いるインスタンスメソッド①を指定
        DateTime dt = DateTime.Now; //現在日付時刻のオブジェクトを得る
        DateTime mydate; //作業用の日付時刻オブジェクトを得る
        mydate = md(dt, 100); //デリゲート経由でMyClass1のCalcメソッドを呼び、現在日付時刻オブジェクトと100を渡す
        Console.WriteLine("今日から100日後は、{0}です", mydate.ToShortDateString());
        md = new MyDelegate(mc2.Calc); //デリゲートに用いるインスタンスメソッド②で置き換える
        mydate = md(dt, 100); //デリゲート経由でMyClass2のCalcメソッドを呼び、現在日付時刻オブジェクトと100を渡す
        Console.WriteLine("今から100時間後は、{0}です", mydate);
        md = new MyDelegate(mc2.Calc2); //【以下追加】デリゲートに用いるインスタンスメソッド③で置き換える
        mydate = md(dt, 100); //デリゲート経由でMyClass2のCalc2メソッドを呼び、現在日付時刻オブジェクトと100を渡す
        Console.WriteLine("今から100日後の100時間後は、{0}です", mydate);
    }
}

p.302 delegate05.cs

//p.302 delegate05.cs
using System;
delegate void MyDG(); //デリゲートの宣言(戻り値無し、引数無し)
class MyClass {
    public void show1() { //デリゲートに用いるインスタンスメソッド①
        Console.WriteLine("show1が呼ばれました");
    }
    public void show2() { //デリゲートに用いるインスタンスメソッド②
        Console.WriteLine("show2が呼ばれました");
    }
    public void show3() { //デリゲートに用いるインスタンスメソッド③
        Console.WriteLine("show3が呼ばれました");
    }
}
class delegate05 {
    public static void Main() {
        MyClass mc = new MyClass(); //デリゲートに用いるメソッドのあるクラス
        MyDG md = new MyDG(mc.show1); //デリゲートに用いるインスタンスメソッド①を指定
        Console.WriteLine("1回目のmd()を実行します");
        md(); //デリゲーションで①を実行
        md += new MyDG(mc.show2); //デリゲートに用いるインスタンスメソッド②を追加
        Console.WriteLine("2回目のmd()を実行します");
        md(); //マルチキャストデリゲーションになり①②を実行
        md += new MyDG(mc.show3); //デリゲートに用いるインスタンスメソッド③を追加
        Console.WriteLine("3回目のmd()を実行します");
        md(); //マルチキャストデリゲーションになり①②③を実行
        md -= new MyDG(mc.show1); //マルチキャストデリゲーションからインスタンスメソッド①を除外
        Console.WriteLine("4回目のmd()を実行します");
        md(); //マルチキャストデリゲーションになり②③を実行
    }
}

アレンジ演習:p.302 delegate05.cs

・一度除外したインスタンスメソッド①を、再度、マルチキャストデリゲーションに追加できるかどうかを確認しよう
 ⇒可能
・また、実行順序がどうなるかも確認しよう
 ⇒追加順なので②③①になる
・それから、追加済のインスタンスメソッド②を、さらに、マルチキャストデリゲーションに追加できるかどうかを確認しよう
 ⇒可能
・また、実行順序がどうなるかも確認しよう
 ⇒追加順なので②③①②になる
・加えて、インスタンスメソッド③を除外し、さらに除外できるかどうかを確認しよう
 ⇒無視される(文法エラーや実行時エラーにはならない)

作成例

//アレンジ演習:p.302 delegate05.cs
using System;
delegate void MyDG(); //デリゲートの宣言(戻り値無し、引数無し)
class MyClass {
    public void show1() { //デリゲートに用いるインスタンスメソッド①
        Console.WriteLine("show1が呼ばれました");
    }
    public void show2() { //デリゲートに用いるインスタンスメソッド②
        Console.WriteLine("show2が呼ばれました");
    }
    public void show3() { //デリゲートに用いるインスタンスメソッド③
        Console.WriteLine("show3が呼ばれました");
    }
}
class delegate05 {
    public static void Main() {
        MyClass mc = new MyClass(); //デリゲートに用いるメソッドのあるクラス
        MyDG md = new MyDG(mc.show1); //デリゲートに用いるインスタンスメソッド①を指定
        Console.WriteLine("1回目のmd()を実行します");
        md(); //デリゲーションで①を実行
        md += new MyDG(mc.show2); //デリゲートに用いるインスタンスメソッド②を追加
        Console.WriteLine("2回目のmd()を実行します");
        md(); //マルチキャストデリゲーションになり①②を実行
        md += new MyDG(mc.show3); //デリゲートに用いるインスタンスメソッド②を追加
        Console.WriteLine("3回目のmd()を実行します");
        md(); //マルチキャストデリゲーションになり①②③を実行
        md -= new MyDG(mc.show1); //マルチキャストデリゲーションからインスタンスメソッド①を除外
        Console.WriteLine("4回目のmd()を実行します");
        md(); //マルチキャストデリゲーションになり②③を実行
        md += new MyDG(mc.show1); //【以下追加】デリゲートに用いるインスタンスメソッド①を再追加
        Console.WriteLine("5回目のmd()を実行します");
        md(); //マルチキャストデリゲーションになり②③①を実行
        md += new MyDG(mc.show2); //【以下追加】デリゲートに用いるインスタンスメソッド②を再追加
        Console.WriteLine("6回目のmd()を実行します");
        md(); //マルチキャストデリゲーションになり②③①②を実行
        md -= new MyDG(mc.show3); //【以下追加】デリゲートに用いるインスタンスメソッド③を除外
        md -= new MyDG(mc.show3); //デリゲートに用いるインスタンスメソッド③をさらに除外
        Console.WriteLine("7回目のmd()を実行します");
        md(); //マルチキャストデリゲーションになり②①②を実行
    }
}

p.304 匿名メソッド

・デリゲートからの呼び出し専用のメソッドであれば、記述の簡略化が可能
・これにより、メソッド名が不要になるので、匿名メソッドという
・生成書式: デリゲート名 参照変数 = delegate(引数型 引数名, …){メソッドの内容};
・匿名メソッドの戻り値型はデリゲートの宣言により決まるので、生成においては指定不要

p.304 anomethed01.cs

//p.304 anomethed01.cs
using System;
delegate int MyDelegate(string s); //デリゲートの宣言(戻り値有り、1引数)
class MyClass {
    public int Show(string s) { //デリゲートに用いるインスタンスメソッド
        Console.WriteLine("{0}と入力されました。", s);
        return 0;
    }
}
class AnoMethed01 {
    public static void Main() {
        MyClass mc = new MyClass();
        Console.Write("文字列入力 --- ");
        string x = Console.ReadLine();
        MyDelegate mdg = new MyDelegate(mc.Show); //従来型
        mdg(x);
        MyDelegate mdg2 = delegate (string i) { //匿名メソッド
            Console.WriteLine("{0}と入力されました。", i);
            return 0;
        };
        mdg2(x);
    }
}

p.306 ラムダ式

・元は関数の簡易書式で、多くの仕掛やプログラム言語に採用されている。
・ただし、活用場面や言語によって文法が異なるので注意。例えば、C#とC++、Javaでは違いがある。
・C#では、主に、匿名メソッドの簡易記述に活用することが多い。
・数学のラムダ式では引数は1つだけだが、C#では、複数の引数を指定できる
・書式: (引数リスト) => 式; //引数リストでは型の記述は不要で、自動的に推定される
・例:
 通常のメソッドで書くと int method(int x, int y) { return x + y; };
 匿名メソッドで書くと   delegate(int x, int y) { return x + y; };
 ラムダ式にすると       (x, y) => { return x + y; } //内容が1文であれば{}は省略可能
・引数が不要の場合、カッコのみを記述する
・例: () => Show(); //定義済のメソッドShow()を呼ぶ
・例: () => Console.Write("OK"); //外部で定義済のメソッドを呼ぶ
・よって、デリゲートへのメソッドの登録において、匿名メソッドの代わりに用いると良い
・例:
 通常のメソッドで書くと デリゲート名 変数名 = new デリゲート名(メソッド名);
 匿名メソッドで書くと   デリゲート名 変数名 = delegate() { メソッド名(); };
 ラムダ式にすると       デリゲート名 変数名 = () => メソッド名();

p.307 lambda01.cs

//p.307 lambda01.cs
using System;
delegate void MyDelegate(); //デリゲートの宣言(戻り値無し、引数無し)
class lambda01 {
    public static void show() {
        Console.WriteLine("呼ばれました");
    }
    public static void Main() {
        show(); // 直接showメソッドを呼び出す
        MyDelegate md = () => show(); // デリゲートのラムダ式による作成
        md(); //デリゲートを通してshowメソッドを実行
    }
}

アレンジ演習:p.307 lambda01.cs

・デリゲートを通してshowメソッドを実行する前に、下記の外部で定義済のメソッドを呼ぶマルチキャストデリゲーションを追加して、
 動作を確認しよう
 md += () => Console.Write("OK");

作成例

//アレンジ演習:p.307 lambda01.cs
using System;
delegate void MyDelegate(); //デリゲートの宣言(戻り値無し、引数無し)
class lambda01 {
    public static void show() {
        Console.WriteLine("呼ばれました");
    }
    public static void Main() {
        show(); // 直接showメソッドを呼び出す
        MyDelegate md = () => show(); // デリゲートのラムダ式による作成
        md += () => Console.WriteLine("OK"); //【追加】ラムダ式でマルチキャストデリゲーションに追加
        md(); //マルチキャストデリゲーションを実行
    }
}

p.308 ラムダ式:引数型の省略

・デリゲートへのメソッドの登録をラムダ式で行うと、デリゲートの宣言で指定した引数型が用いられる
・よって、引数型の指定は不要

p.308 lambda02.cs

//p.308 lambda02.cs
using System;
delegate int MyDelegate(int x, int y); //デリゲートの宣言(戻り値有り、2引数)
class lambda02 {
    public static void Main() {
        MyDelegate md = (x, y) => { return x + y; }; //引数x、yの型は指定不要
        Console.WriteLine("2 + 3 = {0}", md(2, 3)); //デリゲート経由で呼び出す
    }
}

p.308 ラムダ式:マルチキャストデリゲーション

・マルチキャストデリゲーションにおいてもラムダ式の活用は有効

アレンジ演習:p.309 lambda03.cs

・デリゲーションの変数md1、md2を用いずに、mdのみでマルチキャストデリゲーションにしよう

作成例

//アレンジ演習:p.309 lambda03.cs
using System;
delegate void MyDelegate(int x); //デリゲートの宣言(戻り値無し、1引数)
class lambda03 {
    public static void Main() {
        //ラムダ式によるデリゲーションの定義
        MyDelegate md = (int x) => { Console.WriteLine("{0}の2乗は{1}", x, x * x); };
        //ラムダ式によるデリゲーションへの追加
        md += (int x) => { Console.WriteLine("{0}の2倍は{1}", x, x * 2); };
        //マルチキャストデリゲーションの実行
        md(10);
    }
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です