今週の話題

販売本数ランキング 今回トップは「帰ってきた 名探偵ピカチュウ(Switch)」GO!
PS5のアクセシビリティデバイス「Access コントローラー」はゲームにおける身体的ハードルを取り払えるかも【発表会レポ】GO!
PS5新モデル11月10日発売!従来モデルと比較し30%以上小型化、ストレージは1TBに拡大、Ultra HD Blu-rayディスクドライブが着脱可能に GO!

コミュニティ大反発!『ロケットリーグ』ユーザー間でのトレード要素廃止へ―Epic Gamesの不許可と開発元説明 GO!
『ポケモン』“改造ポケモンを貰っても気づかない問題”が海外で議論に―プロは「自分で捕まえたものしか使わない」宣言 GO!
アクティビジョンが『CoD: MW3』発売に向けチーターへ臨戦態勢…一方チート販売業者は「宣伝」のためにツールを無料配布 GO!

基礎コース最終回のコメント

・お疲れ様です!
 自由参加となっていた気がするP254の練習問題2をやってみました。
 問1のコードをオーバーライドではなくて名前の隠ぺいにするという内容でしたが、
 合っているか自信ないです。。。(;^ω^)
 確認よろしくお願いします。

 お疲れ様でした。拝見しました。
 はい、合っています。
 敢えて言えばMain()で
 「A1 a1 = new A1();」とA1オブジェクトを生成した直後に「a1 = a2;」と代入していますが、
 A1オブジェクトを生成する必要はなく「A1 a1;」で必要十分です。
 まとめて「A1 a1 = a2;」としても良いでしょう。

講義メモ

・p.264「interface04.cs」から

p.264 複数のインターフェイスを実装する(同じシグニチャの場合):再掲載

・複数の同じシグニチャの抽象メンバをもつインターフェイスを実装することができる
・この場合、インターフェイスを型とする参照変数を定義して用いると良い
・書式: インターフェイスを型とする参照変数.メソッド名(引数)

補足:p.264 interface04.cs

・005行目:publicは不要
・010行目:publicは不要

p.265 interface05.cs

//p.265 interface05.cs
using System;
interface IMas { //インターフェイス①「です・ます」の定義
    void show(int i); //抽象メソッド①の定義
}
public interface IDa { //インターフェイス②「だ・である」の定義
    void show(int i); //抽象メソッド②の定義 ※①と同じシグニチャ
}
class MyClass : IMas, IDa { //インターフェイス①②を実装するクラス
    void IMas.show(int i) { //インターフェイス①名を前置した抽象メソッド①のオーバーライド「です」
        Console.WriteLine("iは{0}です", i);
    }
    void IDa.show(int i) { //インターフェイス②名を前置した抽象メソッド②のオーバーライド「だ」
        Console.WriteLine("iは{0}だ", i);
    }
}
class interface05 {
    public static void Main() {
        IMas im; //インターフェイス①型の参照変数
        IDa id; //インターフェイス②型の参照変数
        MyClass mc = new MyClass(); //インターフェイス①②を実装するクラスのオブジェクトを生成
        im = mc; //インターフェイス①型の参照変数に代入すると
        im.show(5); //抽象メソッド①のオーバーライド「です」が呼ばれる
        id = mc; //インターフェイス②型の参照変数に代入すると
        id.show(5); //抽象メソッド②のオーバーライド「だ」が呼ばれる
    }
}

アレンジ演習:p.265 interface05.cs

・インターフェイスを実装するクラスのオブジェクトは、インターフェイス型にキャストすることで、インターフェイス型の参照変数で扱える
・このことを活用して、シンプルにしよう
・なお、キャストの優先順位は低くドットの優先順位は高いので、カッコで優先させること
・書式例: ((インターフェイス名)参照変数名).メソッド名(引数,…)

作成例

//p.265 interface05.cs
using System;
interface IMas { //インターフェイス①「です・ます」の定義
    void show(int i); //抽象メソッド①の定義
}
public interface IDa { //インターフェイス②「だ・である」の定義
    void show(int i); //抽象メソッド②の定義 ※①と同じシグニチャ
}
class MyClass : IMas, IDa { //インターフェイス①②を実装するクラス
    void IMas.show(int i) { //インターフェイス①名を前置した抽象メソッド①のオーバーライド「です」
        Console.WriteLine("iは{0}です", i);
    }
    void IDa.show(int i) { //インターフェイス②名を前置した抽象メソッド②のオーバーライド「だ」
        Console.WriteLine("iは{0}だ", i);
    }
}
class interface05 {
    public static void Main() {
        MyClass mc = new MyClass(); //インターフェイス①②を実装するクラスのオブジェクトを生成
        ((IMas)mc).show(5); //【変更】抽象メソッド①のオーバーライド「です」が呼ばれる
        ((IDa)mc).show(5); //【変更】抽象メソッド②のオーバーライド「だ」が呼ばれる
    }
}

p.266 インターフェイスの継承

・クラスがクラスを継承できると同様に、インターフェイスがインターフェイスを継承できる
・これにより、インターフェイスの乱立を防ぐことができる
・継承される基本インタフェースの抽象メンバは、派生インターフェイスに引き継がれる
・定義書式: interface 派生インターフェイス名 : 基本インタフェース名 {…}
※ 継承なので、複数の基本インタフェースを指定することはできない(実装にはならない)

p.266 interface06.cs

//p.266 interface06.cs
using System;
interface IInterface1 { //基本インタフェース
    void setdatano(int n); //抽象メソッド①
    void setdata(double data, int i); //抽象メソッド②
    double calcsum(); //抽象メソッド③
}
interface IInterface2 : IInterface1 { //派生インターフェイス
    //ここに抽象メソッド①「void setdatano(int n);」が継承されている
    //ここに抽象メソッド②「void setdata(double data, int i);」が継承されている
    //ここに抽象メソッド③「double calcsum();」が継承されている
    double calcaverage(); //このインターフェイス独自の抽象メソッド④
}
class MyClass : IInterface2 { //派生インターフェイスを実装するクラス
    double[] data; //実数型配列の宣言
    bool bOK = false; //OKフラグをオフにしておく
    public void setdatano(int n) { //抽象メソッド①のオーバーライド
        data = new double[n]; //引数を要素数として実数型配列を生成
        bOK = true; //OKフラグをオンにする
    }
    public void setdata(double d, int i) { //抽象メソッド②のオーバーライド
        if (!bOK) { //OKフラグがオフであれば
            Console.WriteLine("配列の準備ができていません");
            return; //何もしない
        }
        data[i] = d; //引数dを要素値、iを添字として配列に格納
    }
    public double calcsum() { //抽象メソッド③のオーバーライド
        if (!bOK) { //OKフラグがオフであれば
            Console.WriteLine("配列の準備ができていません");
            return -1.0; //何もしない(仮の値を返す)
        }
        double sum = 0.0; //合計を0にする
        for (int i = 0; i < data.Length; i++) { //全要素について繰返す
            sum += data[i]; //合計に足し込む
        }
        return sum; //合計を返す
    }
    public double calcaverage() { //抽象メソッド④のオーバーライド
        double sum = calcsum(); //合計計算を呼ぶ
        return sum / data.Length; //合計を件数で割って平均を得て返す
    }
}
class interface06 {
    public static void Main() {
        MyClass mc = new MyClass(); //派生インターフェイスを実装するクラスのオブジェクトを生成
        int nNo; //入力用
        while (true) { //無限ループ
            Console.Write("データ数---");
            string strno = Console.ReadLine();
            nNo = Int32.Parse(strno); //整数変換
            mc.setdatano(nNo); //データ数を要素数とする配列を生成
            for (int i = 0; i < nNo; i++) { //データ数について繰返す
                Console.Write("data[{0}] = ", i); //添字を表示
                string strdata = Console.ReadLine();
                mc.setdata(double.Parse(strdata), i); //実数に変換して配列に格納
            }
            Console.WriteLine("合計 = {0}", mc.calcsum());
            Console.WriteLine("平均 = {0}", mc.calcaverage());
            Console.WriteLine();
            Console.Write("続けますか(Y/N)---");
            string yn = Console.ReadLine();
            if (yn == "N" || yn == "n") {
                break;
            }
        }
    }
}

p.270(インターフェイスの継承と名前の隠ぺい)

・派生インターフェイスの抽象メンバと同じシグニチャの仮想メンバがある場合、名前の隠ぺいになる
・よって、newキーワードを前置して、名前の隠ぺいを明示する

p.270 interface07.cs

//p.270 interface07.cs
using System;
interface IMyInterface { //基本インタフェース
    void show1(); //抽象メソッド①
    void show2(); //抽象メソッド②
}
interface IMyInterface2 : IMyInterface { //派生インターフェイス
    //ここに抽象メソッド①が継承されるが、名前の隠ぺいの対象になる
    new void show1(); //抽象メソッド①の名前を隠ぺいする抽象メソッド①'
    //ここに抽象メソッド②が継承される
    void show3(); //抽象メソッド③
}
class MyClass : IMyInterface2 { //派生インターフェイスを実装するクラス
    public void show1() { //抽象メソッド①'のオーバーライド
        Console.WriteLine("show1");
    }
    public void show2() { //抽象メソッド②のオーバーライド
        Console.WriteLine("show2");
    }
    public void show3() { //抽象メソッド③のオーバーライド
        Console.WriteLine("show3");
    }
}
class interface07 {
    public static void Main() {
        MyClass mc = new MyClass(); //派生インターフェイスを実装するクラス
        mc.show1(); //抽象メソッド①'のオーバーライドを呼ぶ
        mc.show2(); //抽象メソッド② のオーバーライドを呼ぶ
        mc.show3(); //抽象メソッド③ のオーバーライドを呼ぶ
    }
}

アレンジ演習:p.270 interface07.cs

・派生インターフェイスにおいて、基本クラスの抽象メソッドと同じシグニチャで戻り値型が異なる抽象メソッドを定義できる
・しかし、この派生インターフェイスを実装するクラスはエラーになってしまう
・その理由を考えよう

作成例

//アレンジ演習:p.270 interface07.cs
using System;
interface IMyInterface { //基本インタフェース
    void show1(); //抽象メソッド①
    void show2(); //抽象メソッド②
}
interface IMyInterface2 : IMyInterface { //派生インターフェイス
    //ここに抽象メソッド①が継承されるが、名前の隠ぺいの対象になる
    new void show1(); //抽象メソッド①の名前を隠ぺいする抽象メソッド①'
    //ここに抽象メソッド②が継承される
    new int show2(); //【追加】抽象メソッド②と同じシグニチャだが戻り値型が異なる抽象メソッド②'
    void show3(); //抽象メソッド③
}
class MyClass : IMyInterface2 { //派生インターフェイスを実装するクラス
    public void show1() { //抽象メソッド①'のオーバーライド
        Console.WriteLine("show1");
    }
    public void show2() { //抽象メソッド②のオーバーライド
        Console.WriteLine("show2");
    }
    public int show2() { //【追加】抽象メソッド②'のオーバーライド⇒戻り値型が異なるが同じシグニチャなのでエラー
        Console.WriteLine("show2");
    }
    public void show3() { //抽象メソッド③のオーバーライド
        Console.WriteLine("show3");
    }
}
class interface07 {
    public static void Main() {
        MyClass mc = new MyClass(); //派生インターフェイスを実装するクラス
        mc.show1(); //抽象メソッド①'のオーバーライドを呼ぶ
        mc.show2(); //抽象メソッド② のオーバーライドを呼ぶ ※ここもエラーになる
        mc.show3(); //抽象メソッド③ のオーバーライドを呼ぶ
    }
}

p.272(インターフェイスの継承と名前の隠ぺいと明示的実装)

・名前の隠ぺいを行っている派生インターフェイスでは、派生クラスの場合と同様に、実装時に隠ぺいされたメンバもオーバライドできる
・これは(baseキーワードではなく)、複数のインターフェイスをクラスが実装する時に同じシグニチャがある場合と同様に「インターフェイス名.」を前置すれば良い
・そして、そのクラスのインスタンスを用いる時に、インターフェイスを型とする参照変数を定義して用いると良い

p.272 interface08.cs

//p.272 interface08.cs
using System;
interface I1 { //基本インターフェイス
    void show1(); //抽象メソッド①
    void show2(); //抽象メソッド②
}
interface I2 : I1 { //派生インターフェイス
    //ここに「void show1();」/抽象メソッド②が継承されるが名前の隠ぺいの対象になる
    new void show1(); //抽象メソッド①の名前の隠ぺいをする抽象メソッド①'
    //ここに「void show2();」/抽象メソッド②が継承される
}
class MyClass : I2 { //派生インターフェイスを実装するクラス
    void I1.show1() { //基本インターフェイス名を前置して抽象メソッド①のオーバライド
        Console.WriteLine("I1.show1");
    }
    void I2.show1() { //派生インターフェイス名を前置して抽象メソッド①'のオーバライド
        Console.WriteLine("I2.show1");
    }
    public void show2() { //基本インターフェイスの抽象メソッド②のオーバライド
        Console.WriteLine("show2");
    }
}
class interface08 {
    public static void Main() {
        MyClass mc = new MyClass(); //派生インターフェイスを実装するクラスのオブジェクトを生成
        I1 i1; //基本インターフェイスを型とする参照変数①
        I2 i2; //派生インターフェイスを型とする参照変数②
        mc.show2(); //基本インターフェイスの抽象メソッド②のオーバライドを呼ぶ
        i1 = mc; //基本インターフェイスを型とする参照変数①にクラスのオブジェクトの参照を代入
        i1.show1(); //基本インターフェイスに対応する抽象メソッド①のオーバライドを呼ぶ
        i2 = mc; //派生インターフェイスを型とする参照変数②にクラスのオブジェクトの参照を代入
        i2.show1(); //派生インターフェイスに対応する抽象メソッド①'のオーバライドを呼ぶ
        i2.show2(); //mc.show2()と同じ
    }
}

アレンジ演習:p.272 interface08.cs

・意味が分かりづらいので、下記に置き換えて考えよう
 ・interface I1 ⇒ interface skyfly //空を飛べる
  ・void show1(); void howtofly() //飛び方を表示(空用)
  ・void show2(); void sound() //飛ぶ時の音を表示
 ・interface I2 ⇒ interface spacefly //宇宙を飛べる(空を飛べるを継承)
  ・void show1(); void howtofly()//飛び方を表示(宇宙用)
 ・class MyClass ⇒ class SpaceDragon : spacefly //宇宙を飛べる宇宙竜
 ・class interface08 も上記に合わせてアレンジしよう

作成例

//アレンジ演習:p.272 interface08.cs
using System;
interface skyfly { //基本インターフェイス「空を飛べる」
    void howtofly(); //抽象メソッド①飛び方を表示(空用)
    void sound(); //抽象メソッド②飛ぶ時の音を表示
}
interface spacefly : skyfly { //派生インターフェイス「宇宙を飛べる(空を飛べるを継承)」
    //ここに「void howtofly();」/抽象メソッド①が継承されるが名前の隠ぺいの対象になる
    new void howtofly(); //抽象メソッド①の名前の隠ぺいをする抽象メソッド①'飛び方を表示(宇宙用)
    //ここに「void sound();」/抽象メソッド②が継承される
}
class SpaceDragon : spacefly { //派生インターフェイス「宇宙を飛べる(空を飛べるを継承)」を実装するクラス「宇宙竜」
    void skyfly.howtofly() { //抽象メソッド①のオーバライド
        Console.WriteLine("翼で空気を受けて飛ぶ"); //飛び方を表示(空用)
    }
    void spacefly.howtofly() { //抽象メソッド①'のオーバライド
        Console.WriteLine("エネルギーを出して飛ぶ"); //飛び方を表示(宇宙用)
    }
    public void sound() { //基本インターフェイスの抽象メソッド②のオーバライド
        Console.WriteLine("ギューン"); //飛ぶ時の音
    }
}
class interface08 {
    public static void Main() {
        SpaceDragon Veldra = new SpaceDragon(); //派生インターフェイスを実装するクラスのオブジェクトを生成
        skyfly Skyfling; //基本インターフェイスを型とする参照変数①
        spacefly Spaceflying; //派生インターフェイスを型とする参照変数②
        Veldra.sound(); //基本インターフェイスの抽象メソッド②のオーバライドを呼ぶ
        Skyfling = Veldra; //基本インターフェイスを型とする参照変数①にクラスのオブジェクトの参照を代入
        Skyfling.howtofly(); //基本インターフェイスに対応する抽象メソッド①のオーバライドを呼ぶ
        ((skyfly)Veldra).howtofly(); //上記はこうしてもOK
        Spaceflying = Veldra; //派生インターフェイスを型とする参照変数②にクラスのオブジェクトの参照を代入
        Spaceflying.howtofly(); //派生インターフェイスに対応する抽象メソッド①'のオーバライドを呼ぶ
        ((spacefly)Veldra).howtofly(); //上記はこうしてもOK
        Spaceflying.sound(); //Veldra.sound()と同じ
    }
}

p.274 練習問題1 ヒント

・抽象メソッド int Count(string str) を持つインターフェイスを定義しよう
・このインターフェイスを実装するクラスを定義しよう
・このクラスの中で抽象メソッドをオーバーライドして、引数strで得られる文字列の長さ(p.73)を返す処理を記述
・実行用クラスとMainメソッドを記述し、上記のクラスのインスタンスを生成し、Countメソッドに文字列を与えよう
・Countメソッドの戻り値を表示して確認しよう

作成例

//p.274 練習問題1
using System;
interface Countable { //インターフェイス「数えられる」
    int Count(string str); //抽象メソッド「文字列の文字数を返す」
}
class CountStrings : Countable { //インターフェイスを実装するクラス「数えられる文字列」
    public int Count(string str) { //抽象メソッド「文字列の文字数を返す」のオーバライド
        return str.Length; //文字数を返す
    }
}
class ex1001 {
    public static void Main() {
        CountStrings wordcount = new CountStrings(); //インターフェイスを実装するクラスのオブジェクトを生成
        string word = "ABC";
        Console.WriteLine("{0}は{1}文字です", word, wordcount.Count(word));
    }
}

p.254 練習問題1 ヒント

・仮想メソッドCalcは「2つのint型の引数をとり、その和を返す」のだから、シグニチャは
 int Calc(int ●, int ▲);
・クラスA1は仮想メソッドCalcを持ち、●+▲を返す
・クラスA2はクラスA1を継承する
・クラスA2は継承したメソッドCalcをオーバーライドし、●-▲を返す
・Mainでは:
 ① クラスA2のインスタンスを生成
 ② ①の参照変数.Calcメソッドの実行結果を表示(例:Calc(5, 8)) ⇒ 差になる
 ③ クラスA1型の参照変数を①の参照変数で初期化
 ④ ③の参照変数.Calcメソッドの実行結果を表示(例:Calc(5, 8)) ⇒ やはり差になる(多態性)

作成例

//p.254 練習問題1
using System;
class A1 { //基本クラス
    public virtual int Calc(int a, int b) { //オーバライド可能な仮想メソッド
        return a + b; //和を返す
    }
}
class A2 : A1 { //派生クラス
    public override int Calc(int a, int b) { //オーバライドメソッド
        return a - b; //差を返す
    }
}
class ex0901 {
    public static void Main() {
        A2 a2 = new A2(); //派生クラスA2のインスタンスを生成
        Console.WriteLine(a2.Calc(5, 8)); //オーバライドメソッドを実行
        A1 a1 = a2; //A1型変数a1を宣言して。A2への参照a2を代入
        Console.WriteLine(a1.Calc(5, 8)); //仮想メソッドを実行しても…
    }
}

以上です。お疲れ様でした。

今週の話題

販売本数ランキング 今回トップは「ピクミン4(Switch)」が奪還GO!
『ブルアカ』が好調のネクソン、期待の新作2本を投入し大幅増収なるか【ゲーム企業の決算を読む】 GO!
TGS 2023サイドイベント「Tokyo Blockchain Game Blast with CoinMusme」開催決定 GO!
「未来」を共有してチームが一丸に。ゲーム制作の現場で生かされる「むきなおり」とは【CEDEC 2023】GO!
N対N双方向リアルタイム通信基盤のDiarkis、「東京ゲームショウ2023」ビジネスデイに出展 GO!

動機「趣味の一環」で一巻の終わり…『シュタゲ』ネタバレで逮捕の被告に懲役2年執行猶予5年、罰金100万円の有罪判決 GO!

前回のコメント

・インターフェイスの例えがとてもわかりやすくて内容がすっと入ってきました。

 何よりです。

・ちなみに飛行機のとき例えてくれたBなんとか・・とはなんでしょうか(=゚ω゚)ノ

 わかりづらくてごめんなさい。B787ですね。
 ボーイング787という主流になりつつある新型航空機です。

講義メモ 第10章 インターフェイス

p.255 インターフェイスとは

・元の意味は「異なる要素を関連付けるための仕組み」「接合口」「接合面」など
・C#などのオブジェクト指向言語では「継承関係のないクラスを関連付けるための仕組み」
・例えば「飛べるもの」インタフェースを持つドラゴンクラスと飛行機クラスを記述し、これらのオブジェクトを「飛べるもの」として
 扱える

p.255 インターフェイスの宣言

・書式: interface インターフェイス名 {…}
・インタフェースには抽象メソッド、抽象プロパティ、抽象インデクサを記述できる
※ Java等とは異なり、データメンバh記述できない
・よって、抽象クラスをより徹底したものになっている
・インターフェイス内の抽象メンバでは「abstract」指定は不用(「virtual」も不要)
・抽象プロパティ、抽象インデクサはget、setの内容を記述しないもの
・インターフェイスの抽象プロパティの書式: 型 プロパティ名 { get; set; }
・インターフェイスの抽象インデクサの書式: 型 this[インデックスの型 インデックス] { get; set; }
・どちらも、getまたはsetを省略できる(通常、setを省略して書き換え不可に)

p.256 インターフェイスの実装

・クラスにおける継承を、インターフェイスでは実装という
・1クラスは1クラスしか継承できないが、1クラスが複数のインターフェイスを実装できる
・書式: class クラス名 : インターフェイス名,… {…}
 例: class Dragon : Flyable, Swimable {…} //ドラゴンは飛べる&泳げる
・抽象クラスを継承した派生クラスと同様に、抽象メソッドをもつインターフェイスを実装したクラスは、抽象メソッドをオーバーライド
 する必要があるが、overrideキーワードは不要で、publicにすること。

p.257 3行目 正誤

【誤】インターフェイスを実装するメソッドは必ずpublicとなります。
【正】インターフェイスの抽象メンバをオーバーライドするメンバは必ずpublicとなります。

p.257 interface01.cs

//p.257 interface01.cs
using System;
interface IMyInterface { //インターフェイスの定義
    void show(string str); //抽象メソッド
    int xprop { get; set; } //抽象プロパティ
    int this[int i] { get; set; } //抽象インデクサ
}
class MyClass : IMyInterface { //インターフェイスを実装するクラス
    protected int i; //※このprotectedは特に意味はない
    int[] arr = new int[10]; //インデクサ用
    public void show(string str) { //抽象メソッドのオーバーライド(override不用)
        Console.WriteLine(str);
    }
    public int xprop { //抽象プロパティのオーバーライド(override不用)
        get { return i; }
        set { i = value; }
    }
    public int this[int index] { //抽象インデクサのオーバーライド(override不用)
        get { return arr[index]; }
        set { arr[index] = value;   }
    }        
}
class interface01 {
    public static void Main() {
        MyClass mc = new MyClass(); //実装クラスのオブジェクトを生成
        mc.show("Test Interface"); //オーバーライドメソッドを呼ぶ
        mc.xprop = 100; //オーバーライドプロパティのsetを呼ぶ
        Console.WriteLine("mc.xprop = {0}", mc.xprop); //オーバーライドプロパティのgetを呼ぶ
        for (int i = 0; i < 10; i++) { //全要素について繰返す 
            mc[i] = i * 2; //オーバーライドインデクサのsetを呼ぶ
        }
        for (int i = 0; i < 10; i++) { //全要素について繰返す 
            Console.WriteLine("mc[{0}] = {1}", i, mc[i]); //オーバーライドインデクサのgetを呼ぶ
        }
    }
}

p.258 下7行 正誤

・4つある「仮想」はすべて「抽象」の誤り
【誤】インターフェイスの実装メンバがすべてpublic
【正】インターフェイスの抽象メンバをオーバーライドするメンバがすべてpublic
※ インターフェイスの抽象メンバをオーバーライドすることを「実装する」という場合もある

p.359 1つのインターフェイスを複数のクラスで実装する

・1つのインターフェイスを複数のクラスで実装することが可能
・こうすることで、継承関係のない複数のクラスをインターフェイスによって関連付けることができる

p.261 interface02.cs

//p.261 interface02.cs
using System;
interface IMyInterface { //インターフェイスの定義
    void show(string str); //抽象メソッド
}
class MyClass : IMyInterface { //インターフェイスを実装するクラス①
    public void show(string s) { //抽象メソッドのオーバーライド①
        Console.WriteLine(s);
    }
}
class YourClass : IMyInterface { //インターフェイスを実装するクラス②
    public void show(string x) { //抽象メソッドのオーバーライド②
        Console.WriteLine("{0}が入力されました", x);
    }
}
class interface02 {
    public static void Main() {
        MyClass mc = new MyClass(); //実装クラス①のオブジェクトを生成
        YourClass yc = new YourClass(); //実装クラス②のオブジェクトを生成
        mc.show("abc"); //抽象メソッドのオーバーライド①を呼ぶ
        yc.show("abc"); //抽象メソッドのオーバーライド②を呼ぶ
    }
}

p.260(インターフェイスを型とする参照変数)

・インターフェイスはクラスとは異なり、オブジェクトを生成できない
・しかし、同じくオブジェクトを生成できない抽象クラスと同様に、インスタンスの型になることは可能
・よって、インターフェイスを実装するクラスのオブジェクトは、インスタンスを型とする参照変数で扱える
・例: 飛べるインターフェイスを実装するドラゴンクラスのオブジェクト「ヴェルドラ」を「飛べるもの A」と扱える
・例: 飛べるインターフェイスを実装する飛行機クラスのオブジェクト「B787」を「飛べるもの B」と扱える

p.261 interface03.cs

//p.261 interface03.cs
using System;
interface IMyInterface { //インターフェイスの定義
    int calc(int x, int y); //抽象メソッド
}
class Plus : IMyInterface { //インターフェイスを実装するクラス①
    public int calc(int a, int b) { //抽象メソッドのオーバーライド①
        return a + b; //和を返す
    }
}
class Minus : IMyInterface { //インターフェイスを実装するクラス②
    public int calc(int a, int b) { //抽象メソッドのオーバーライド②
        return a - b; //差を返す
    }
}
class interface03 {
    public static void Main() {
        IMyInterface im; //インターフェイス型の参照変数
        Plus p = new Plus(); //実装クラス①のオブジェクトを生成
        Minus m = new Minus();//実装クラス②のオブジェクトを生成
        im = p; //インターフェイスを実装するクラスのオブジェクトは、インターフェイスを型とする参照変数で扱える
        Console.WriteLine("im.calc = {0}", im.calc(3, 5)); //抽象メソッドのオーバーライド①を呼ぶ
        im = m; //同上
        Console.WriteLine("im.calc = {0}", im.calc(3, 5)); //抽象メソッドのオーバーライド②を呼ぶ
    }
}

アレンジ演習:p.261 interface03.cs

・よって、インターフェイスを型とする配列も宣言でき、要素として、実装クラスのオブジェクトを格納できる
・インターフェイス型の参照変数を配列にしてみよう

作成例

//アレンジ演習:p.261 interface03.cs
using System;
interface IMyInterface { //インターフェイスの定義
    int calc(int x, int y); //抽象メソッド
}
class Plus : IMyInterface { //インターフェイスを実装するクラス①
    public int calc(int a, int b) { //抽象メソッドのオーバーライド①
        return a + b; //和を返す
    }
}
class Minus : IMyInterface { //インターフェイスを実装するクラス②
    public int calc(int a, int b) { //抽象メソッドのオーバーライド②
        return a - b; //差を返す
    }
}
class interface03 {
    public static void Main() {
        //インターフェイス型の配列に実装クラスのオブジェクトを順に生成して格納
        IMyInterface[] im = { new Plus(), new Minus() }; 
        foreach (var w in im) { //配列の全要素について繰返す
            Console.WriteLine("im.calc = {0}", w.calc(3, 5)); //抽象メソッドのオーバーライドを呼ぶ
        }
    }
}

p.263 複数のインターフェイスを実装する

・1クラスは1クラスしか継承できないが、1クラスが複数のインターフェイスを実装できる
・書式: class クラス名 : インターフェイス名,… {…}

p.263 interface04.cs 010行目 正誤

【誤】interface ISecond : IFirst
【正】interface ISecond
※ インターフェイスの継承(p.266)になっているが、このプログラムでは不要
※ エラーにはならないが、MyClassで2つのインターフェイスを実装する意味がなくなってしまう

p.263 interface04.cs

//p.263 interface04.cs
using System;
interface IFirst { //インターフェイスの定義①
    void show(int x); //抽象メソッド①
}
interface ISecond { //インターフェイスの定義②
    void show(int x, int y); //抽象メソッド②
}
class MyClass : IFirst, ISecond { //複数のインターフェイスを実装するクラス
    public void show(int x) { //抽象メソッド①のオーバライド
        Console.WriteLine("x = {0}", x);
    }
    public void show(int x, int y) { //抽象メソッド②のオーバライドで結果はオーバーロードになる
        Console.WriteLine("x = {0}, y = {1}", x, y);
    }
}
class interface04 {
    public static void Main() {
        MyClass mc = new MyClass(); //実装クラス①のオブジェクトを生成
        mc.show(2); //抽象メソッド①のオーバライドを実行
        mc.show(1, 3); //抽象メソッド②のオーバライドを実行
    }
}

p.264 複数のインターフェイスを実装する(同じシグニチャの場合)

・複数の同じシグニチャの抽象メンバをもつインターフェイスを実装することができる
・この場合、インターフェイスを型とする参照変数を定義して用いると良い
・書式: インターフェイスを型とする参照変数.メソッド名(引数)
例:
interface 空中を飛べる { void 飛び方(); }
interface 宇宙を飛べる { void 飛び方(); }
class スペースドラゴン : 空中を飛べる, 宇宙を飛べる {
 空中を飛べる 空中用; //インターフェイスを型とする参照変数①を定義
 宇宙を飛べる 宇宙用; //インターフェイスを型とする参照変数②を定義
 public void 空中用.飛び方() { Console.Write("火を噴きながら飛ぶ"); } //参照変数①を利用
 public void 宇宙用.飛び方() { Console.Write("静かに飛ぶ"); } //参照変数②を利用
}

提出:アレンジ演習:p.261 interface03.cs

講義メモ

p.245「inheritance07.cs」から

提出フォロー:アレンジ演習:p.243 inheritance06.cs

・3つのクラスのコンストラクタの引数をすべて「int i」にしたい
・xの初期値をiで与えるとする
・派生クラスのコンストラクタと、派生の派生クラスのコンストラクタにbaseキーワードを追加しよう
・Mainで派生の派生クラスのコンストラクタに引数で90を与えてみよう

作成例

//アレンジ演習:p.243 inheritance06.cs
using System;
class MyBase { //基本クラス
    protected int x; //継承可能なデータメンバ
    public MyBase(int i) { //①引数の有るコンストラクタ
        Console.WriteLine("ここはMyBase");
        x = i; //データメンバの初期化
    }
}
class Derived1 : MyBase { //派生クラス
    //ここに「protected int x;」があるとみなされる
    public Derived1(int i) : base(i) { //②引数の有るコンストラクタ
        //この直前に基本クラスの①引数の有るコンストラクタが実行される
        Console.WriteLine("ここはDerived1");
        x = i; //データメンバの初期化
    }
}
class Derived2 : Derived1 { //派生の派生クラス
    //ここに「protected int x;」があるとみなされる
    public Derived2(int i) : base(i)  {  //③引数の有るコンストラクタ
        //この直前に基本クラスの①引数の有るコンストラクタが実行される
        //この直前に派生クラスの②引数の有るコンストラクタが実行される
        Console.WriteLine("ここはDerived2");
        x = i; //データメンバの初期化
    }
    public void show() { //派生の派生クラスの独自のメソッド
        Console.WriteLine("x = {0}", x);
    }
}
class inheritance06 {
    public static void Main() {
        Derived2 d2 = new Derived2(90); //派生の派生クラスのインスタンスを生成(①②③の実行)
        d2.show(); //③が最後に動くので90になっている
    }
}

p.245 inheritance07.cs

//p.245 inheritance07.cs
using System;
class MyBase { //基本クラス
    protected double d; //非公開だが継承可能なデータメンバ
    public MyBase(double a, double b, double c) { //判別式を表すコンストラクタ
        d = Math.Pow(b, 2.0) - 4.0 * a * c; //bの2乗-4acを返す
    }
}
class MyJudge : MyBase { //派生クラス
    public bool bJudge; //判定結果
    public MyJudge(double p, double q, double r)
        : base(p, q, r) { //MyBaseクラスのコンストラクタに引数を渡す
        //ここで「d = Math.Pow(q, 2.0) - 4.0 * p * r;」が動作する
        Console.WriteLine("判別式 = {0}", d); //よってdは計算済
        if (d < 0.0)
            bJudge = false;
        else
            bJudge = true;
    }
}
class inheritance07 {
    public static void Main() {
        MyJudge mj = new MyJudge(1.0, 2.0, 3.0); //派生クラスのコンストラクタに3値を渡す
        Console.WriteLine(mj.bJudge); //コンストラクタで格納された判定結果を表示
        MyJudge mk = new MyJudge(1.0, 4.0, 0.0); //派生クラスのコンストラクタに3値を渡す
        Console.WriteLine(mk.bJudge); //コンストラクタで格納された判定結果を表示
    }
}

p.247 抽象クラス(抽象メソッド)

・基本クラスの下に次々と派生クラスを作っていくと、具体的な内容を記述するのは派生クラスになり、基本クラスは共通する枠組みだけに
 なることが多い
・この場合に、シグニチャのみを基本クラスに記述したものが、抽象メソッドで、abstractキーワードを付けて示す。
・書式: astruct 戻り値型 メソッド名(引数リスト);
・基本クラスで抽象メソッドを描くことで、派生クラスでオーバーライドして、その内容を書くことが義務になるので、基本クラスによって
 まとめて扱うことが保証されるのがメリット
・なお、抽象メソッドはインスタンスメソッドのみで、静的メソッドにはできない

p.248 抽象クラス

・抽象メソッドを1つでも含むクラスは抽象クラスとなり、abstractキーワードを付けて示す。
・書式: astruct class クラス名 {…}
・抽象メソッドは具体性がないので、抽象クラスはインスタンスの生成ができない
 例:Slimeを派生クラスとする基本クラスMonsterは抽象クラスにすると良い。

p.248 abstract01.cs

//p.248 abstract01.cs
using System;
abstract class MyAb { //抽象クラス
    //Hanbetsuメソッドは抽象メソッド
    public abstract double Hanbetsu(double a, double b, double c);
}
class MyHanbetsu : MyAb { //派生クラス
    //基本クラスの抽象メソッドは必ずオーバーライドします
    //もしこのクラスでオーバーライドしないと、このクラス自身が抽象クラスになる
    public override double Hanbetsu(double a, double b, double c) { //抽象メソッドのオーバーライド
        return Math.Pow(b, 2.0) - 4.0 * a * c;
    }
}
class abstract01 {
    public static void Main() {
        MyHanbetsu h = new MyHanbetsu(); //派生クラスのインスタンスは生成できる
        //MyAb ma = new MyAb(); //抽象クラスのインスタンスは生成できない
        double d = h.Hanbetsu(1.0, 2.0, 3.0); //オーバーライドメソッドを呼ぶ
        Console.WriteLine(d);
    }
}

アレンジ演習:p.248 abstract01.cs

・抽象クラスに通常のメソッドも記述できることを確認しよう
・また、派生クラスで抽象メソッドをしないことで派生クラスも抽象クラスになることを確認しよう
 ※ 派生の派生クラスを追記して、抽象メソッドをオーバーライドして、Mainで用いること

作成例

//アレンジ演習:p.248 abstract01.cs
using System;
abstract class MyAb { //抽象クラス
    public abstract double Hanbetsu(double a, double b, double c); //抽象メソッド
    public virtual void show() { Console.WriteLine(this); } //通常の仮想メソッド
}
abstract class MyAb2 : MyAb { //派生クラスだが抽象クラス
    //ここに「public abstract double Hanbetsu(double a, double b, double c);」があるとみなす
    public override void show(){ Console.WriteLine(this); } //通常のオーバーライドメソッド
}
class MyHanbetsu : MyAb2 { //派生の派生クラス
    //ここに「public override void show(){…}」があるとみなす
    public override double Hanbetsu(double a, double b, double c) { //抽象メソッドのオーバーライド
        return Math.Pow(b, 2.0) - 4.0 * a * c;
    }
}
class abstract01 {
    public static void Main() {
        MyHanbetsu h = new MyHanbetsu(); //派生クラスのインスタンスは生成できる
        //MyAb ma = new MyAb(); //抽象クラスのインスタンスは生成できない
        double d = h.Hanbetsu(1.0, 2.0, 3.0); //オーバーライドメソッドを呼ぶ
        Console.WriteLine(d);
        h.show();
    }
}

p.249(sealedクラス)

・継承は強力な機能であり、基本クラスの機能を獲得できてしまう。
・これを防ぐ必要があるクラスは、sealedキーワードを前置してsealedクラスにすると良い
・C#が提供しているクラスの何もsealedクラスは多数ある
・当然、sealedクラスには抽象メソッドは記述できない

p.250 クラスを分割定義する

・partialキーワードをクラス定義に前置することで、クラスをファイルをまたがって記述できる
・これにより、見通しが良くなり、1クラスをグループ開発できるようになる
・分割したすべてのソースにおいてpartialキーワードが必要
・実行時に自動的に結合されるので、どれかに記述されたメンバは、ほかの部分から利用できる
・Visul Studioでは、同じプロジェクトの中に複数のソースファイルを作成し、クラスを分割配置できることに加えて、
 partialクラスを分割配置できる
・p.250 partial01.csのように、1ソース内でpartialクラスを記述することも可能(あまり意味はない)

p.250 partial01.cs

//p.250 partial01.cs
using System;
partial class MyClass { //分割したクラス定義①
    public int x;
}
class partial01 {
    public static void Main() {
        MyClass mc = new MyClass();
        mc.x = 10;
        mc.Show();
    }
}
partial class MyClass { //分割したクラス定義②
    public void Show() {
        Console.WriteLine("x = {0}", x);  //分割したクラス定義①にあるデータメンバを利用可能
    }
}

アレンジ演習:p.250 partial01.cs

・「分割したクラス定義②」を別ファイルにしよう
・Visual Studio 2022では自動的に「using System;」が補われる

作成例:partial01.cs

//p.250 partial01.cs
using System;
partial class MyClass { //分割したクラス定義①
    public int x;
}
class partial01 {
    public static void Main() {
        MyClass mc = new MyClass();
        mc.x = 10;
        mc.Show();
    }
}

作成例:CodeFile1.cs

using System;
partial class MyClass { //分割したクラス定義②
    public void Show() {
        Console.WriteLine("x = {0}", x);  //分割したクラス定義①にあるデータメンバを利用可能
    }
}

p.251 メソッドを分割定義する

・分割クラスにおいては、メソッドも分割できる
・ただし、戻り値型がvoidでアクセス修飾子がpublicであること
※ しかし、メソッドの内容を分割クラスにまたがって記述できるわけではないので実質的には不用

<自由参加課題> p.251 練習問題

今週の話題

販売本数ランキング 今回トップは「ARMORED CORE VI FIRES OF RUBICON(アーマード・コアVI ファイアーズオブルビコン)(PS5)」GO!
PlayStation Plusの12ヶ月分サブスクの価格が9月6日より値上げへ。最上位のプレミアムは1万3900円に GO!
gumi、営業黒字転換も厳しい決算内容―期待作『アスタータタリクス』で一発逆転を狙う【ゲーム企業の決算を読む】GO!
持続可能なブロックチェーンゲームは実現できるのか?―「ゲームとしての面白さ」が経済を支える【CEDEC 2023】GO!
PS5向けデバイス「PlayStation Portal リモートプレーヤー」が11月15日に発売―今月末より予約も開始 GO!
NPCが「本物の人間」になる日!?AIの進化はゲーム内登場キャラの「自由行動」を可能にするか【CEDEC 2023】GO!

『ポケモンGO』のアートワークにAI疑惑―海外メディアからの質問にも開発は曖昧な回答 GO!
最新作『CoD: MW3』AIが有害なボイスチャット検出―英語対象にテストを行い対応言語拡張も予定 GO!