・テキスト編「第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);
}
}