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 { public string HowToFly() { return "エンジンで";} } //F16戦闘機は「飛べるもの」
作成例
//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 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.259 1つのインターフェイスを複数のクラスで実装する
・継承関係にないクラスであっても、同じインターフェイスを実装していれば、関連付けられる
p.259 interface02.cs
//p.259 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 1つのインターフェイスを複数のクラスで実装する つづき
・クラスを型とする変数が定義できるように、インターフェイスを型とする変数が定義できる ・ただし、インターフェイスは抽象なのでインスタンスを生成できない ・インターフェイスを実装したクラスのオブジェクトを、インターフェイスを型とする変数で扱える ・例: Dragon Veldra = new Dragon(); //ドラゴンのヴェルドラを生成 Flyable obj1 = Veldra; //ヴェルドラを飛行物体1とする F16 Masotan = new F16(); //F16戦闘機のマソタンを生成 Flyable obj2 = Masotan; //マソタンを飛行物体2とする
p.261 interface03.cs
//p.261 interface03.cs
using System;
interface IMyInterface { //インターフェイスの定義
int calc(int x, int y); //抽象メソッド「xとyで計算した結果を返す」
}
class Plus : IMyInterface { //インターフェイスを実装したクラス①
public int calc(int a, int b) { //抽象メソッドをオーバーライドして①
return a + b; //「aとbで計算した結果を返す」→「aとbの和を返す」とした
}
}
class Minus : IMyInterface { //インターフェイスを実装したクラス②
public int calc(int a, int b) { //抽象メソッドをオーバーライドして②
return a - b; //「aとbで計算した結果を返す」→「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); //抽象メソッド「xとyで計算した結果を返す」
}
class Plus : IMyInterface { //インターフェイスを実装したクラス①
public int calc(int a, int b) { //抽象メソッドをオーバーライドして①
return a + b; //「aとbで計算した結果を返す」→「aとbの和を返す」とした
}
}
class Minus : IMyInterface { //インターフェイスを実装したクラス②
public int calc(int a, int b) { //抽象メソッドをオーバーライドして②
return a - b; //「aとbで計算した結果を返す」→「a引くbを返す」とした
}
}
class interface03 {
public static void Main() {
IMyInterface[] im = new IMyInterface[2]; //インターフェイス型の配列
Plus p = new Plus(); //インターフェイスを実装したクラス①のオブジェクト①
Minus m = new Minus(); //インターフェイスを実装したクラス②のオブジェクト②
im[0] = p; //オブジェクト①をインターフェイス型の配列[0]に代入
im[1] = m; //オブジェクト②をインターフェイス型の配列[1]に代入
foreach (var i in im) {
Console.WriteLine("im.calc = {0}", i.calc(3, 5)); //インターフェイス型の変数経由でメソッド①②を
}
}
}
p.263 複数のインターフェイスを実装する
・クラスの継承と異なり、インターフェイスの実装は複数行って良い
正誤:p.263 interface04.cs 010行目
・「: IFirst」は無用(インターフェイスの継承(p.266)になってしまう)
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 複数のインターフェイスを実装する・つづき
・複数のインターフェイスを実装する場合に、双方に同じシグニチャのメソッドがあるとエラーにはならないが、
オーバーライドにおいて区別する必要がある
・定義のメソッド名の前に「インターフェイス名.」を付記すれば良い
・書式: 戻り値型 インターフェイス名.メソッド名(引数型 引数名,…) {…}
・なお、この場合「public」は不要
正誤:p.264 interface05.cs 005行目,010行目
・どちらも先頭の「public」は無用
p.264 interface05.cs
//p.264 interface05.cs
using System;
public 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.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 { //派生インターフェース
double calcaverage(); //抽象メソッド④
}
class MyClass : IInterface2 { //派生インターフェースを実装するクラス
double[] data;
bool bOK = false;
public void setdatano(int n) { //抽象メソッド①のオーバーライド
data = new double[n];
bOK = true;
}
public void setdata(double d, int i) { //抽象メソッド②のオーバーライド
if (!bOK) {
Console.WriteLine("配列の準備ができていません");
return;
}
data[i] = d;
}
public double calcsum() { //抽象メソッド③のオーバーライド
if (!bOK) {
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;
}
}
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.266 インターフェイスの継承・つづき
・基本インタフェースの抽象メソッドと同じシグニチャの抽象メソッドが派生インタフェースにある場合、名前の隠蔽が起こる ・よって、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.266 インターフェイスの継承・つづき
・基本インタフェースの抽象メソッドと同じシグニチャの抽象メソッドが派生インタフェースにある場合、名前の隠蔽が起こる ・この場合、「インターファイス名.」をつける(明示的実装という)ことで双方をオーバーライドすることもできる
p.272 interface08.cs
//p.272 interface08.cs
using System;
interface I1 { //基本インターフェース
void show1(); //抽象メソッド①
void show2(); //抽象メソッド②
}
interface I2 : I1 { //派生インターフェース
new void show1(); //抽象メソッド③=①と同じシグニチャなので名前の隠蔽になる
}
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(); //抽象メソッド②のオーバーライドメソッドが呼ばれる
}
}