講義メモ:memo20250914.txt
以上となります。
講義の進行にご協力をありがとうございました。
このサイトは「ある限り」残しておきますが、いつか消えます。ご了承ください。
for trainees of Hirano's class only
講義メモ:memo20250914.txt
以上となります。
講義の進行にご協力をありがとうございました。
このサイトは「ある限り」残しておきますが、いつか消えます。ご了承ください。
p.266 インターフェイスの継承
・クラスと同様にインターフェイスが継承が可能 ・継承される側を基本インターフェイス、する側を派生インターフェイスと呼ぶ ・クラスと同様に継承できるインターフェイスは1つのみで、派生インターフェイスの継承は可能
アレンジ演習:p.266 interface06.cs
・派生インターフェイスIInterface2を継承するIInterface3を追加できることを確認しよう ※中身は自由
作成例
//アレンジ演習: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(); //抽象メソッド④
}
interface IInterface3 : IInterface2 { //【以下追加】派生の派生インターフェイス③
//ここに void setdatano(int n); //抽象メソッド①があるとみなされる
//ここに void setdata(double data, int i); //抽象メソッド②があるとみなされる
//ここに double calcsum(); //抽象メソッド③があるとみなされる
//ここに double calcaverage(); //抽象メソッド④があるとみなされる
double calcmax(); //抽象メソッド⑤
}
class MyClass : IInterface3 { //【変更】派生の派生インターフェイス③を実装するクラス
double[] data;
bool bOK = false; //OKフラグ(false:準備前)
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;
}
public double calcsum() { //抽象メソッド③のオーバーライド
if (!bOK) { //OKフラグが準備前?
Console.WriteLine("配列の準備ができていません");
return -1.0; //値を返す必要があるので、ダミーの値を返す
}
double sum = 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; //件数で割って平均値を得て返す
}
public double calcmax() { //【以下追加】抽象メソッド⑤のオーバーライド
if (!bOK) { //OKフラグが準備前?
Console.WriteLine("配列の準備ができていません");
return -1.0; //値を返す必要があるので、ダミーの値を返す
}
double max = -1.0; //最大値用(※仮に正の数限定とする)
for (int i = 0; i < data.Length; i++) { //全要素について繰返す
max = (max > data[i]) ? max : data[i]; //最大値を更新
}
return max; //最大値を返す
}
}
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("最大 = {0}", mc.calcmax()); //【追加】
Console.WriteLine();
Console.Write("続けますか(Y/N)---");
string yn = Console.ReadLine();
if (yn == "N" || yn == "n") {
break;
}
}
}
}
p.270 インターフェイスの継承:同じシグニチャの抽象メンバがある場合
・派生インターフェイスにおいて、基本インターフェイスと同じシグニチャの抽象メンバがある場合、名前の隠ぺいが起こる ・この場合、newキーワードを追記して、名前の隠ぺいであることを明示すると良い
アレンジ演習:p.270 interface07.cs
・派生インターフェイスIMyInterface2を継承するIMyInterface3を追加しよう ・この中で、Show2()メソッドの名前の隠ぺいができることを試そう
作成例
//アレンジ演習:p.270 interface07.cs
using System;
interface IMyInterface { //基本インターフェイス①
void show1(); //抽象メソッド①
void show2(); //抽象メソッド②
}
interface IMyInterface2 : IMyInterface { //派生インターフェイス②
//ここに「void show1(); //抽象メソッド①」があるとみなされるが隠ぺいされる
new void show1(); //抽象メソッド①の名前の隠ぺい
//ここに「void show2(); //抽象メソッド②」があるとみなされる
void show3(); //抽象メソッド③
}
interface IMyInterface3 : IMyInterface2 { //【以下追加】派生の派生インターフェイス③
//ここに「void show1(); //抽象メソッド①」があるとみなされるが隠ぺいされる
//ここに「new void show1(); //抽象メソッド①の名前の隠ぺいがあるとみなされる
//ここに「void show2(); //抽象メソッド②」があるとみなされるが隠ぺいされる
new void show2(); //抽象メソッド②の名前の隠ぺい
//ここに「void show3(); //抽象メソッド③」があるとみなされる
}
class MyClass : IMyInterface3 { //【変更】派生の派生インターフェイス③を実装するクラス
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.266 インターフェイスの継承:名前の隠ぺい時に隠ぺいされたメソッドをオーバーライドする
・複数のインターフェイスを実装する時に、同じシグニチャのメソッドが含まれている場合と同様に 「インターフェイス名.メソッド名」と記述すれば、名前の隠ぺい時に隠ぺいされたメソッドをオーバーライドすることができる ・そして、利用時にインターフェイスを型とする参照変数を経由することで識別が可能
アレンジ演習:p.272 interface08.cs
・派生インターフェイスI2を継承するI3を追加しよう ・この中で、Show2()メソッドの名前の隠ぺいを行う ・MyClassクラスは派生の派生インターフェイスI3を実装するようにしよう
作成例
//アレンジ演習:p.272 interface08.cs
using System;
interface I1 { //基本インターフェイス①
void show1(); //抽象メソッド①
void show2(); //抽象メソッド②
}
interface I2 : I1 { //派生インターフェイス②
new void show1(); //抽象メソッド①の名前の隠ぺい
}
interface I3 : I2 { //【追加】派生の派生インターフェイス③
new void show2(); //抽象メソッド②の名前の隠ぺい
}
class MyClass : I3 { //【変更】派生の派生インターフェイス②を実装するクラス
void I1.show1() { //隠ぺいされた基本インターフェイス①の抽象メソッド①のオーバーライド
Console.WriteLine("I1.show1");
}
void I2.show1() { //隠ぺいした派生インターフェイス②の抽象メソッド①のオーバーライド
Console.WriteLine("I2.show1");
}
void I1.show2() { //【変更】基本インターフェイス①の抽象メソッド②のオーバーライド
Console.WriteLine("I1.show2");
}
void I3.show2() { //【追加】隠ぺいした派生の派生インターフェイス③の抽象メソッド②のオーバーライド
Console.WriteLine("I3.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(); //派生インターフェイス②の抽象メソッド②のオーバーライドを呼ぶ事も可能
//【以下追加】
I3 i3; //派生の派生インターフェイス③を型とする参照変数
i3 = mc; //派生の派生インターフェイス②を型とする参照変数を経由することで
i3.show2(); //派生の派生インターフェイス③の抽象メソッド②のオーバーライドを呼ぶ事も可能
}
}
p.274 練習問題 ex1001.cs ヒント
・インターフェイス名を MyInterface とする ・これを実装するクラスを MyClass とする ・文字数を返すには「return str.Length;」で良い ・ex1001クラスにMainメソッドをおいて、MyClassのオブジェクトを生成してCountメソッドを試せば良い
作成例
//p.274 練習問題 ex1001.cs
using System;
interface MyInterface { //インターフェイス
int Count(string str); //抽象メソッド
}
class MyClass : MyInterface { //インターフェイスを実装するクラス
public int Count(string str) { //抽象メソッドのオーバーライド
return str.Length; //引数で受け取った文字列の文字数を返す
}
}
class ex1001 {
public static void Main() {
MyClass mc = new MyClass(); //インターフェイスを実装するクラスのオブジェクトを生成
Console.WriteLine(mc.Count("Hello")); //抽象メソッドのオーバーライドを呼ぶ
}
}
・p.259「1つのインターフェイスを複数のクラスで実装する」から
提出フォロー:アレンジ演習:p.257 interface01.cs
・IMyInterfaceインターフェイスを実装するYourClassを追加し、MainメソッドでMyClassと同様に扱えることを試そう
作成例
//アレンジ演習: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 { //インターフェイスを実装したクラス①
int i;
int[] arr = new int[10];
public void show(string str) { //抽象メソッドのオーバーライド①
Console.WriteLine(str);
}
public int xprop { //抽象プロパティのオーバーライド①
get { return i; } set { i = value; }
}
public int this[int index] { //抽象インデクサのオーバーライド①
get { return arr[index]; } set { arr[index] = value; }
}
}
class YourClass : IMyInterface { //【以下追加】インターフェイスを実装したクラス② ※上記と同一内容
int i;
int[] arr = new int[10];
public void show(string str) { //抽象メソッドのオーバーライド②
Console.WriteLine(str);
}
public int xprop { //抽象プロパティのオーバーライド②
get { return i; } set { i = value; }
}
public int this[int index] { //抽象インデクサのオーバーライド②
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)のオーバーライド①を呼ぶ
}
//【以下追加】上記のクラス名、変数名を替えたもので同一内容
YourClass md = new YourClass(); //インターフェイスを実装したクラス②のインスタンスを生成
md.show("Test Interface 2"); //抽象メソッドのオーバーライド②を呼ぶ
md.xprop = 100; //抽象プロパティ(set)のオーバーライド②を呼ぶ
Console.WriteLine("md.xprop = {0}", md.xprop); //抽象プロパティ(get)のオーバーライド②を呼ぶ
for (int i = 0; i < 10; i++) {
md[i] = i * 2; //抽象インデクサ(set)のオーバーライド②を呼ぶ
}
for (int i = 0; i < 10; i++) {
Console.WriteLine("md[{0}] = {1}", i, md[i]); //抽象インデクサ(get)のオーバーライド②を呼ぶ
}
}
}
p.259 1つのインターフェイスを複数のクラスで実装する
・1つのインターフェイスを複数のクラスで実装でき、抽象メンバ(メソッド、プロパティ、インデクサ)をそれぞれにおいてオーバーライドする
アレンジ演習 p.257 interface02.cs
・IMyInterfaceインターフェイスに、以下の抽象プロパティを追加し、各クラスでオーバーライドしよう
int xprop { get; set; }
・オーバーライドの内容は自由
/【以下追加】pre>
作成例
//アレンジ演習 p.257 interface02.cs
using System;
interface IMyInterface { //インターフェイスの定義
void show(string str); //抽象メソッド
int xprop { get; set; } //【追加】抽象プロパティ
}
class MyClass : IMyInterface { //インターフェイスを実装するクラス①
int x; //【追加】
public void show(string s) {
Console.WriteLine(s);
}
public int xprop { //【追加】抽象プロパティのオーバーライド①
get { return x; } set { x = value; }
}
}
class YourClass : IMyInterface { //インターフェイスを実装するクラス②
int y; //【追加】
public void show(string x) {
Console.WriteLine("{0}が入力されました", x);
}
public int xprop { //【追加】抽象プロパティのオーバーライド②
get { return y; } set { y = value; }
}
}
class interface02 {
public static void Main() {
MyClass mc = new MyClass(); // インターフェイスを実装するクラス①から生成
YourClass yc = new YourClass(); // インターフェイスを実装するクラス②から生成
mc.show("abc"); //①でオーバーライドしているメソッドを呼ぶ
yc.show("abc"); //②でオーバーライドしているメソッドを呼ぶ
//【以下追加】
mc.xprop = 10; //①でオーバーライドしているプロパティのsetを呼ぶ
Console.WriteLine(mc.xprop); //①でオーバーライドしているプロパティのgetを呼ぶ
yc.xprop = 30; //②でオーバーライドしているプロパティのsetを呼ぶ
Console.WriteLine(yc.xprop); //②でオーバーライドしているプロパティのgetを呼ぶ
}
}
p.260 1つのインターフェイスを複数のクラスで実装する:インターフェイスを型とする参照変数
・インターフェイスも、抽象クラスもインスタンスを生成することはできない ・しかし、インターフェイスや、抽象クラスを型とする参照変数は宣言できる ・この参照変数に、インターフェイスを実装するオブジェクトや、派生クラスのオブジェクトを代入することで、参照をコピーすることができる ・よって、インターフェイスを実装する複数のクラスに対して、オーバーライドしているメンバを同じ形式で呼び出すことができる
アレンジ演習:p.261 interface03.cs
・インターフェイスIMyInterfaceを型とする配列iaを生成しよう ・そして、変数p、mを格納して、配列の要素経由でCalcメソッドを実行してみよう
作成例
//アレンジ演習: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)); //参照変数経由でメソッド①を実行
//【以下追加】インターフェイスを型とする配列に上記を格納
IMyInterface[] ia = {p, m};
foreach (var i in ia) { //全要素について繰返す
Console.WriteLine("i[].calc = {0}", i.calc(3, 5)); //
}
}
}
p.263 複数のインターフェイスを実装する
・クラスが複数のクラスを継承することはできない ・しかし、複数のインターフェイスを実装することはできる
p.263【正誤】interface04.cs
・010行:「: IFirst」はインターフェイスの継承にあたるが、ここでは不要
アレンジ演習:p.263 interface04.cs
・IFirstインターフェースのみを実装するYourClassを追加し、Show(int)メソッドを適当にオーバーライドする ・YourClassのオブジェクトycを生成し、IFirstインターフェースを型とする配列に、mcと共に格納できるか確認しよう
作成例
//アレンジ演習: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 YourClass : IFirst { //【以下追加】インターフェース①のみを実装するクラス②
public void show(int x) { //抽象メソッド①のオーバーライド
Console.WriteLine("x = {0}", x);
}
}
class interface04 {
public static void Main() {
MyClass mc = new MyClass();
mc.show(2); //抽象メソッド①のオーバーライドを呼ぶ
mc.show(1, 3); //抽象メソッド②のオーバーライドを呼ぶ
//【以下追加】
YourClass yc = new YourClass();
IFirst[] ifa = {mc, yc}; //インターフェイス①を型とする配列に格納
foreach (var w in ifa) { //この配列の全要素について繰返す
w.show(100); //抽象メソッド①のオーバーライドを呼ぶ
}
}
}
p.263 複数のインターフェイスを実装する:同じシグニチャのメソッドが含まれている場合
・同じシグニチャのメソッドが含まれている場合、その両方をオーバーライドする必要がある
・そこで、識別するために「戻り値型 インターフェイス名.メソッド名(){…}」として記述する
・この時、publicは不要(記述するとエラーになる)
・呼び出す側では、インターフェイス型の参照変数を用いることで、どちらかを識別できる
p.264【正誤】interface05.cs
・005行:「public」は不要 ・010行:「public」は不要
アレンジ演習:p.264 interface05.cs
・インターフェイス型の参照変数を用いずに「mc.Show(5);」と呼び出すとどうなるか確認しよう
作成例
//p.264 interface05.cs
using System;
interface IMas { //インターフェイス①
void show(int i); //抽象メソッド①
}
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); //インターフェイス②の抽象メソッド②のオーバーライドが実行
//mc.show(5); //文法エラーになる
}
}
・クラス・メソッドの分割定義ですが、どういう時に行うのでしょうか。そして、最大何回分割できるのでしょうか。
クラスの定義内容が大きく、長いソースになる場合、
加えて、内容の更新が頻繁に起こるメソッドがある場合、クラス・メソッドの分割定義を検討します。
ソースを分けることにより、役割分担がしやすくなり、見通しが良くなることから、
可読性や保守性が向上します。
しかし、ソースの管理が煩雑になりますので(分割回数に制限はありませんが)
適切な回数にとどめておくと良いでしょう。
・抽象メソッドは基本クラスで宣言をし、派生クラスで実装する必要があるとのことですが、つまりは基本クラスで宣言のみをしてもあまり意味がないという事でしょうか?もしくはそもそも実行できないのでしょうか?
抽象メソッドは「実行用」ではなく「定義用」です。
よって、抽象メソッドを含む抽象クラスは(インターフェイスも)インスタンスを生成できません。
抽象クラスは、主に、派生クラスが複数ある場合に、まとめて扱うための存在です。
そして、派生クラスごとに内容は異なるが、同じシグニチャのメソッドを、
抽象メソッドとして、基本クラス(抽象クラス)に記述しておくことで、
各クラスの整合性を保ち、利用しやすくします。
例えば、Monsterクラスを基本クラス(抽象クラス)として「自分の情報を表示する」Showメソッドを
持たせる場合、表示内容はモンスターの種類により異なるので、抽象メソッドにしておきます。
そして、Monsterクラスの派生クラスのSlimeクラスでShowメソッドを「HPを表示する」とし、
同じく、Monsterクラスの派生クラスのDragonクラスでShowメソッドを「HP,MPを表示する」として
記述します。
すると、SlimeクラスのインスタンスもDragonクラスのインスタンスも、
Monsterクラスのインスタンスとして扱うことができ、どちらもShowメソッドがあることが、
確定します。
このことを、各クラスを使う側から見ると
「Monsterクラスの派生クラスのオブジェクトには、必ずShowメソッドがあるから安心」
「まとめて扱える」というメリットになるわけです。
この例をプログラムにすると以下のようになります。
using System;
abstract class Monster { //抽象クラスMonster
protected int hp = 1; //モンスター共通のインスタンス変数の定義
public abstract void Show(); //抽象メソッド(内容は決められないメソッド)
}
class Slime : Monster { //Monsterの派生クラス①
public override void Show() { //↑のオーバライド
Console.WriteLine("HPは{0}", hp);
}
}
class Dragon : Monster { //Monsterの派生クラス②
int mp = 100;
public override void Show() { //↑のオーバライド
Console.WriteLine("HPは{0}、MPは{1}", hp, mp);
}
}
class abstract01 {
public static void Main() {
Slime slalin = new Slime(); //Slimeクラスのオブジェクト「スラリン」を生成
Dragon veldra = new Dragon(); //Dragonクラスのオブジェクト「ヴェルドラ」を生成
Monster m1 = slalin, m2 = veldra; //上記をMonsterクラスのオブジェクトm1,m2として扱う
m1.Show(); //Monsterクラスの派生クラスなら必ずShowメソッドがある(しかも多態性が起こる)
m2.Show(); //同上
//【以下参考】まとめてあつかえる例
Slime rimuru = new Slime(); //Slimeクラスのオブジェクト「リムル」を生成
Monster[] ms = {slalin, veldra, rimuru}; //上記をMonsterクラスの配列に格納
foreach (Monster w in ms) { //すると、派生クラスのオブジェクトをまとめて扱える
Console.Write("クラスは{0}で、",w);
w.Show(); //必ずShowメソッドがあるので呼び出せる(しかも多態性が起こる)
}
}
}
次回(最終回)予告:p.259 1つのインターフェイスを複数のクラスで実装する
講義メモ:memo20250907.txt
p.255 インターフェイス
・元の意味は「接合部/面」で「異なる要素を関連付ける仕掛け」
・C#では、継承関係にないクラスであっても、関連付けられる仕組み
・例えば、空中シューティングゲームで、飛んでいるドラゴンとF16戦闘機を「飛べるもの」インターフェイスで
まとめて扱うことができる
・インターフェイスは文法では抽象クラスの拡張形式で「抽象メンバのみをもった参照型」となる
・書式: interface インターフェイス名 { 抽象メンバ … }
・インターフェイスに記述できる抽象メンバとしては、抽象メソッド、抽象プロパティ、抽象インデクサがある
※ (Javaとは異なり)データメンバは記述できない
・例: interface Flyable { string HowToFly(); } //「飛べるもの」インターフェイスに飛び方を返すメソッドがある
・なお、インターフェイスの中に記述した抽象メンバは「abstract」は不要
・インターフェイスの中の抽象メソッドの書式: 戻り値型 メソッド名(引数型 引数,…);
・インターフェイスの中の抽象プロパティの書式: データ型 プロパティ名 { get; set; }
・インターフェイスの中の抽象インデクサの書式: データ型 this[インデックス型 インデックス名 { get; set; }
p.256 インターフェイスの実装
・クラスの継承と同じ形式で、インターフェイスの実装が可能
・なお、クラスの継承と異なり、インターフェイスの実装は複数行って良い
・書式: class クラス名 : インターフェイス名,… {…}
・例:
class F16 : Flyable {…} //F16戦闘機は「飛べるもの」
・例:
class Dragon : Flyable, Swimable {…} //ドラゴンは「飛べるもの」で「泳げるもの」
・インターフェイスを実装したクラスでは、インターフェイスにある抽象メンバをオーバーライドする必要がある
・しかし、これは継承におけるオーバーライドとは区別され、override指定は不要で、通常、publicにする
・例:
class F16 : Flyable { //F16戦闘機は「飛べるもの」
public string HowToFly() {
return "エンジンで";
}
}
・例:
class Dragon : Flyable, Swimable { //ドラゴンは「飛べるもの」で「泳げるもの」
public string HowToFly() {
return "自分の翼で";
}
public string HowToSwim() {
return "自分の尾で";
}
}
アレンジ演習:p.257 interface01.cs
・IMyInterfaceインターフェイスを実装するYourClassを追加し、MainメソッドでMyClassと同様に扱えることを試そう
提出:アレンジ演習:p.257 interface01.cs
p.250 クラスを分割定義する
・1クラスを複数のソースリストで記述できる ・これにより、ソースの長大化を防いだり、チーム開発を効率化できる ・テキストではクラス定義を2分割して1ソースに置いているが、通常、ソースの分割に用いる ・クラスを分割定義するには、classの前に partial を付記する(これを部分クラスともいう) ・部分クラスのどこかで定義したメンバは、他のソースにある部分クラスからも利用可能 ・ビルドにより分割クラスは1つになり、1つの実行ファイルが生成される ・なお、クラスを分割定義している場合は、VisualStudioの「ファイル」「すべて保存」を用いて、一部ファイルの保存モレを防止すると良い
アレンジ演習:p.250 partial01.cs
・018行目までをpartial01.csに、020行目以降をpartial01a.csに分離しよう ・なお、「using System;」は両方またはどちらか片方で必要に応じて配置すること ・1つのプロジェクトにpartial01.csファイルがある状態で、そこに、ソースファイルを追加すれば良い
作成例:partial01.cs(分割元)
//アレンジ演習: partial01.cs
using System;
partial class MyClass { //部分クラス MyClassクラス①
public int x;
}
class partial01 { //通常クラス
public static void Main() {
MyClass mc = new MyClass(); //部分クラスを結合した状態で扱う
mc.x = 10;
mc.Show();
}
}
作成例:partial01.cs(分割先)
//アレンジ演習: partial01a.cs
using System; //こちらにも必要(自動生成されることもある)
partial class MyClass { //部分クラス MyClassクラス②
public void Show() {
Console.WriteLine("x = {0}", x);
}
}
p.251 メソッドを分割定義する
・分割したクラスにおいて、メソッドの宣言と定義を別々の部分クラスに記述できる
・これを部分メソッドといい、メソッドの冒頭に「partial」を指定する
・ただし、戻り値型がvoidで、privateなメソッドに限る
・部分メソッド宣言の書式: partial void メソッド名(引数型 引数名, …);
・部分メソッド定義の書式: partial void メソッド名(引数型 引数名, …){…}
※ この仕掛けは未完成のメソッドがある場合に便利で、 シグニチャだけを部分メソッドとして記述しておくと良い
※ 1メソッドを複数ソースファイルで分割記述できるわけではない
アレンジ演習:p.251 partialmethod01.cs
・020行目までをpartialmethod01.csに、021行目以降をpartialmethod01a.csに分離しよう ・なお、「using System;」は両方またはどちらか片方で必要に応じて配置すること ・1つのプロジェクトにpartialmethod01a.csファイルがある状態で、そこに、ソースファイルを追加すれば良い
作成例:partialmethod01.cs(分割元)
// partialmethod01.cs
using System;
partial class Partialmethod01 {
public static void Main() {
Console.WriteLine("Mainメソッドです");
Partialmethod01 pm = new Partialmethod01();
pm.Func1();
pm.Func2("テスト");
}
//ここではメソッドの中身を記述していない
//戻り値はvoid型、privateでなくてはだめ
partial void Func1(); //宣言のみ
partial void Func2(string s); //宣言のみ
}
作成例:partialmethod01a.cs(分割先)
using System;
partial class Partialmethod01 {
partial void Func1(){ //定義を含む
Console.WriteLine("パーシャルメソッド1です");
}
}
partial class Partialmethod01 {
partial void Func2(string s) { //定義を含む
Console.WriteLine("引数は「{0}」です", s);
}
}
p.254 ex0901.cs/ex0902.cs 練習問題1,2:ヒント
・p.231 override01.csをベースにすると良い ・Mainメソッドの作例: A2 a2 = new A2(); //派生クラスのインスタンスを生成 Console.WriteLine(a2.Calc(5, 8)); //差を表示? A1 a1 = a2; //基本クラス型の変数にインスタンスを代入 Console.WriteLine(a1.Calc(5, 8)); //和を表示? 差を表示?
作成例: 練習問題1
//p.254 ex0901.cs 練習問題1
using System;
class A1 { //基本クラス
public virtual int Calc(int x, int y) { //オーバーライドを許可する仮想メソッド
return x + y; //和を返す
}
}
class A2 : A1 { //派生クラス
//ここに「public virtual int Calc(int x, int y){…}」があるが、下記にオーバーライドされる
public override int Calc(int x, int y) { //オーバーライドメソッド
return (x > y) ? x - y : y - x; //差を返す
}
}
class ex0901 {
public static void Main() {
A2 a2 = new A2(); //派生クラスのインスタンスを生成
Console.WriteLine("a2.Calc(10, 30) = {0}", a2.Calc(10, 30)); //差になる
A1 a1 = a2; //基本クラスの整数にインスタンスを渡す
Console.WriteLine("a1.Calc(10, 30) = {0}", a1.Calc(10, 30)); //多態性で差になる
}
}
作成例: 練習問題2
//p.254 ex0902.cs 練習問題2
using System;
class A1 { //基本クラス
public int Calc(int x, int y) { //通常メソッド(継承可能)
return x + y; //和を返す
}
}
class A2 : A1 { //派生クラス
//ここに「public int Calc(int x, int y){…}」があるが、下記に「名前の隠ぺい」される
new public int Calc(int x, int y) { //名前の隠ぺい
return (x > y) ? x - y : y - x; //差を返す
}
}
class ex0901 {
public static void Main() {
A2 a2 = new A2(); //派生クラスのインスタンスを生成
Console.WriteLine("a2.Calc(10, 30) = {0}", a2.Calc(10, 30)); //差になる
A1 a1 = a2; //基本クラスの整数にインスタンスを渡す
Console.WriteLine("a1.Calc(10, 30) = {0}", a1.Calc(10, 30)); //多態性がなく和になる
}
}
提出フォロー:アレンジ演習:p.240 inheritance05.cs
・派生クラスのオーバーライドメソッドを、そのまま、派生の派生クラスでオーバーライドできることを確認しよう ・Derived2クラスを継承するDerived3クラスを定義して、Show()メソッドをオーバーライドし、Main()で呼び出てみよう
作成例
//アレンジ演習:p.240 inheritance05.cs
using System;
class MyBase { //基本クラス
protected int x = 10; //継承可能なインスタンス変数
public virtual void show() { //仮想メソッド
Console.WriteLine("x = {0}", x);
}
}
class Derived1 : MyBase { //派生クラス
// ここに「protected int x = 10; //継承可能なインスタンス変数」が継承される
protected int y = 20; //継承可能なインスタンス変数
// ここに「public virtual void show() … //仮想メソッド」が継承される
}
class Derived2 : Derived1 { //派生の派生クラス
// ここに「protected int x = 10; //継承可能なインスタンス変数」が継承される
// ここに「protected int y = 20; //継承可能なインスタンス変数」が継承される
int z = 30; //継承されないインスタンス変数
// ここに「public virtual void show() … //仮想メソッド」が継承されるがオーバライドされる
public override void show() { //オーバライドメソッド
Console.WriteLine("z = {0}", z);
}
}
class Derived3 : Derived2 { //【以下追加】派生の派生の派生クラス
// ここに「protected int x = 10; //継承可能なインスタンス変数」が継承される
// ここに「protected int y = 20; //継承可能なインスタンス変数」が継承される
// ここに「public virtual void show() … //仮想メソッド」が継承されるがオーバライドされる
public override void show() { //オーバライドメソッド
Console.WriteLine("x = {0}, y = {1}", x, y);
}
}
class inheritance05 {
public static void Main() {
MyBase mb; //基本クラス型の変数
Derived1 d1 = new Derived1(); //派生クラスのインスタンス
Derived2 d2 = new Derived2(); //派生の派生クラスのインスタンス
mb = d1; //派生クラスのインスタンスを基本クラス型で扱う
mb.show(); //オーバライドなので多態性が発動し派生クラスのメソッドを実行
mb = d2; //派生の派生クラスのインスタンスを基本クラス型で扱う
mb.show(); //オーバライドなので多態性が発動し派生クラスのメソッドを実行
//【以下追加】
Derived2 d3 = new Derived3(); //派生の派生の派生クラスのインスタンス
d3.show(); //オーバライドメソッドを実行
}
}
p.243 クラスの継承とコンストラクタ
・どのクラスにおいても、コンストラクタ(p.167)を記述しないと、自動的に引数のないコンストラクタが用意される
・これは派生クラスでも同様
・しかし、派生クラスのコンストラクタの前には自動的に基本クラスのコンストラクタが実行される
・これにより、基本クラスから継承したデータメンバの初期化を基本クラスのコンストラクタに任させることができる
・基本クラスに引数を持つコンストラクタがあり、派生クラスにも引数を持つコンストラクタがある場合、引数を受け渡す必要があるので、
baseキーワードを用いる
・書式: public 派生クラス名(型 引数,…) : base (受け取りたい引数,…) {…}
p.243 inheritance06.cs
//p.243 inheritance06.cs
using System;
class MyBase { //基本クラス
protected int x; //継承可能なインスタンス変数
public MyBase() { //④基本クラスの引数無コンストラクタ
Console.WriteLine("ここはMyBase"); //⑤
x = 10; //⑥
}
}
class Derived1 : MyBase { //派生クラス
public Derived1() { //③派生クラスの引数無コンストラクタ
//ここで基本クラスの引数無コンストラクタが実行される
Console.WriteLine("ここはDerived1"); //⑦
x = 20; //⑧
}
}
class Derived2 : Derived1 { //派生の派生クラス
public Derived2() { //②派生の派生クラスの引数無コンストラクタ
//ここで派生クラスの引数無コンストラクタが実行される(その中で基本クラスの引数無コンストラクタが実行される)
Console.WriteLine("ここはDerived2"); //⑨
x = 30; //⑩
}
public void show() { //⑫自前のメソッド
Console.WriteLine("x = {0}", x); //⑬
}
}
class inheritance06 {
public static void Main() {
Derived2 d2 = new Derived2(); //①派生の派生オブジェクトを生成&コンストラクタを呼ぶ
d2.show(); //⑪派生の派生オブジェクトのメソッドを呼ぶ
}
}
アレンジ演習:p.243 inheritance06.cs
・Derived1クラスにprivateなint型のインスタンス変数yを追加 ・また、yに40を代入し、その旨を表示する処理をコンストラクタに追加
作成例
//アレンジ演習:p.243 inheritance06.cs
using System;
class MyBase { //基本クラス
protected int x; //継承可能なインスタンス変数
public MyBase() { //④基本クラスの引数無コンストラクタ
Console.WriteLine("ここはMyBase"); //⑤
x = 10; //⑥
}
}
class Derived1 : MyBase { //派生クラス
private int y;
public Derived1() { //③派生クラスの引数無コンストラクタ
//ここで基本クラスの引数無コンストラクタが実行される
Console.WriteLine("ここはDerived1"); //⑦
x = 20; //⑧
Console.WriteLine("yに40を代入"); //【追加】
y = 40; //【追加】
}
}
class Derived2 : Derived1 { //派生の派生クラス
public Derived2() { //②派生の派生クラスの引数無コンストラクタ
//ここで派生クラスの引数無コンストラクタが実行される(その中で基本クラスの引数無コンストラクタが実行される)
Console.WriteLine("ここはDerived2"); //⑨
x = 30; //⑩
}
public void show() { //⑫自前のメソッド
Console.WriteLine("x = {0}", x); //⑬
}
}
class inheritance06 {
public static void Main() {
Derived2 d2 = new Derived2(); //①派生の派生オブジェクトを生成&コンストラクタを呼ぶ
d2.show(); //⑪派生の派生オブジェクトのメソッドを呼ぶ
}
}
アレンジ演習:p.245 inheritance07.cs
・テキストp.246下から4行目の例のとおりに、コンストラクタを書換え、呼び出し部分などもアレンジしてみよう
作成例
//アレンジ演習: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; //判別式の値を返す
}
}
class MyJudge : MyBase { //派生クラス
public bool bJudge; //内部用のフラグ変数
public MyJudge(int x, double p, double q, string str, double r) //【変更】派生クラスの引数有コンストラクタ
: base(p, q, r) { //MyBaseクラスのコンストラクタに引数を渡す
//ここで基本クラスの引数有コンストラクタが実行される(引数は↑から受け取る)
Console.WriteLine("判別式 = {0}", d); //基本クラスの引数有コンストラクタによりdに代入
if (d < 0.0) {
bJudge = false;
} else {
bJudge = true;
}
Console.WriteLine("x = {0}, str = {1}", x, str); //【追加】基本クラスの引数有コンストラクタによりdに代入
}
}
class inheritance07 {
public static void Main() {
MyJudge mj = new MyJudge(100, 1.0, 2.0, "OK", 3.0); //【変更】コンストラクタに5値を渡す
Console.WriteLine(mj.bJudge);
MyJudge mk = new MyJudge(200, 1.0, 4.0, "NG", 0.0); //【変更】コンストラクタに5値を渡す
Console.WriteLine(mk.bJudge);
}
}
p.247 抽象クラス:補足
・p.256にある通り、抽象メソッドと同様に、抽象プロパティ、抽象インデクサを記述できる
・抽象プロパティの書式: abstruct 戻り値型 プロパティ名{get; set;}; //getとsetのどちらかは省略可
・抽象インデクサの書式: abstruct 戻り値型 this[インデクス型 インデクス名]{get; set;}; //getとsetのどちらかは省略可
p.248 abstract01.cs
//p.248 abstract01.cs
using System;
abstract class MyAb { //抽象クラスMyAb
public abstract double Hanbetsu(double a, double b, double c); //抽象メソッド
}
class MyHanbetsu : MyAb { //MyAbの派生クラス
//ここに「public abstract double Hanbetsu(double a, double b, double c);」があるとみなされる
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() {
//MyAb y = new MyAb(); //【確認】抽象クラスのインスタンスは生成不可
MyHanbetsu h = new MyHanbetsu(); //派生クラスのインスタンスを生成
double d = h.Hanbetsu(1.0, 2.0, 3.0); //オーバライドメソッドを実行
Console.WriteLine(d);
}
}
アレンジ演習:p.248 abstruct01.cs
・抽象クラスMyAbに抽象プロパティage(戻り値型:int、get/set有)を追加する ・すると、MyHanbetuクラスが抽象クラスになり、そのままではエラーになる ・そこで、MyHanbetuクラスの派生クラスYourHanbetuを追記し、抽象プロパティageをオーバーライドしよう ・そして、Mainメソッドにおいて、派生クラスYourHanbetuを使うようしてエラーを解消しよう
作成例
//アレンジ演習:p.248 abstract01.cs
using System;
abstract class MyAb { //抽象クラスMyAb
public abstract double Hanbetsu(double a, double b, double c); //抽象メソッド
public abstract int age { get; set;} //【追加】抽象プロパティ
}
abstract class MyHanbetsu : MyAb { //【変更】MyAbの派生抽象クラス
//ここに抽象メソッド「public abstract double Hanbetsu(double a, double b, double c);」があるとみなされる
public override double Hanbetsu(double a, double b, double c) { //↑のオーバライド
return Math.Pow(b, 2.0) - 4.0 * a * c;
}
//ここに抽象プロパティ「public abstract int age { get; set;}」があるとみなされる
}
class YourHanbetsu : MyHanbetsu { //【以下追加】派生の派生クラス
int yourage = 0;
//ここに抽象メソッド「public abstract double Hanbetsu(double a, double b, double c);」があるとみなされる
//ここにオーバライド「public override double Hanbetsu(double a, double b, double c) {}」があるとみなされる
//ここに抽象プロパティ「public abstract int age { get; set;}」があるとみなされる
public override int age { get { return yourage; } set { yourage = value;} } //↑のオーバライド
}
class abstract01 {
public static void Main() {
//MyHanbetsu h = new MyHanbetsu(); //【削除】派生クラスのインスタンスを生成
YourHanbetsu h = new YourHanbetsu(); //【差替】派生の派生クラスのインスタンスを生成
double d = h.Hanbetsu(1.0, 2.0, 3.0); //オーバライドメソッドを実行
Console.WriteLine(d);
Console.WriteLine(h.age); //【追加】オーバーライドプロパティを呼ぶ
}
}
p.249 抽象クラス:sealedクラスについて
・抽象クラスとは逆の概念で、継承を禁止するにはsealedクラスにすると良い ・継承は便利だが、publicメンバが無条件に引き継がれてしまうので、これを避けたい場合に用いる ・なお、C#が提供するクラスの中にも、sealedクラスがあり、継承しようとするとエラーになる sealedクラスの例: Stringクラス、Int32クラス、など sealedではないクラスの例:System.Collections.ArrayListクラス(p.177) など ・ちなみに、静的クラス(p.204)も継承できない
ゲームソフト販売本数ランキング:今週1位は「METAL GEAR SOLID Δ: SNAKE EATER(PS5)」 GO!
『ブルアカ』『メイプルストーリー』減衰…ネクソンの日本事業は上期25%の減収で折り返し【ゲーム企業の決算を読む】GO!
プロが教える動画制作、中高生向け無料オンライン講座9-10月 GO!
【キャリアクエスト】FGOだけじゃない、ラセングルならではの挑戦を求めて。“若い会社”で活躍する、とあるゼネラリストの働き方事情 GO!
インディーゲームの祭典「INDIE Live Expo」開催日は11月29日に決定、100作品以上の紹介やアワードを予定 GO!
Roblox、ユーザーに対しての「年齢確認」取り組み強化へ―未成年と大人のコミュニケーションを大きく制限する機能も追加予定 GO!
R-18コンテンツを開いているブラウザタブを検知・スクショするマルウェア Webカムで撮影も 性的脅迫に使用か GO!
App Storeの審査が突如通らず──DMM傘下のスマホゲームでトラブル 「リリースから実装済みの全イラストが修正対象に」GO!
「みんなのGOLF」新作、発売日に異例声明 ユーザーからの“不具合報告”続出で 「現象の原因究明に努める」GO!