メンバとメソッド C言語の構造体 変数の集まり C#言語のクラス + それを処理する関数の集まり フィールド または メンバ変数 メンバ 二つあわせてクラスのメンバと呼ぶ C言語の構造体 変数の集まり C#言語のクラス + それを処理する関数の集まり フィールド または メンバ変数 メンバ と呼ぶ メソッド または メンバ関数 と呼ぶ
カプセル化 オブジェクトのデータと その処理方法を 一つのオブジェクトに 閉じ込めること カプセル化とは? オブジェクトのデータ (メンバ変数) オブジェクトの処理方法 (メソッド)
クラスとインスタンス クラス 設計図 (1枚だけ) オブジェクト(大量) クラス変数 = インスタンス インスタンスの利用 = メソッド class Time { public int h; public int m; ・・・ } 設計図 (1枚だけ) オブジェクト(大量) Time t1 = new Time(); Time t2 = new Time(); クラス変数 = インスタンス t1.Input(); t1.hとt1.mに値を入力 t2.Input(); t2.hとt2.mに値を入力 インスタンスの利用 = メソッド オブジェクトの 利用
なぜカプセル化が必要か? できれば,オブジェクトの内部構造を利用者に知られたくない プログラマAさん (オブジェクトの設計者) プログラマBさん (オブジェクトの利用者) できれば,オブジェクトの内部構造を利用者に知られたくない オブジェクトの内部構造は別に知りたくない.利用方法さえ分れば良い. デバッグのため,あるいは性能向上のため,時々オブジェクトの内部構造を変更したい オブジェクトの内部構造を変更しても,利用方法は変えて欲しくない.今までのソースのままでオブジェクトを使いたい オブジェクト 設計者しかアクセスできないメンバ → private指定 利用者が利用するメンバ → public指定
C#言語ソースプログラムの原型(5) publicの代わりに キーワードprivate を指定したら・・・ using System; class Time { private int h; int m; public void Input() Console.Write("時? "); h = int.Parse(Console.ReadLine()); Console.Write("分? "); m = int.Parse(Console.ReadLine()); } public void Write() Console.Write("{0}時{1}分\n", h, m); class mihon41 { static void Main() Time t1 = new Time(); Time t2 = new Time(); t1.Input(); t2.Input(); Console.Write("一つ目は"); t1.Write(); Console.Write("二つ目は"); t2.Write(); Console.Write("{0}時", t1.h); Console.Write("{0}分\n", t1.m); } privateメンバ変数は読み出しも代入もできない 何も指定がない場合もprivateメンバと見なされる pubilcもprivateも指定しないと・・・ publicメンバ(メンバ変数とメソッド)→外部からアクセス可能 privateメンバ(メンバ変数とメソッド)→外部からアクセス不可
publicメンバとprivateメンバ 設計者が書いたソース 利用者が書いたソース using System; class Time { private int h; private int m; private void Adjust() while (m > 60) h = h + 1; m = m - 60; } public void Add(Time t) h = h + t.h; m = m + t.m; Adjust(); public void Input() Console.Write("時? "); h = int.Parse(Console.ReadLine()); Console.Write("分? "); m = int.Parse(Console.ReadLine()); public void Write() Console.Write("{0}時{1}分\n", h, m); class mihon41 { static void Main() Time t1 = new Time(); Time t2 = new Time(); t1.Input(); t2.Input(); Console.Write("一つ目は"); t1.Write(); Console.Write("二つ目は"); t2.Write(); t1.Add(t2); Console.Write("足すと"); t1.Write(); t.Adjust(); } privateメソッドの定義. mが60以上にならないように調整するメソッド publicメソッドの呼び出し 引数(Timeクラス変数)tとこのオブジェクトの和を再びこのオブジェクトに代入するpublicメソッド t1.h = t1.h + t2.h t1.m = t1.m + t2.m 時? 10 分? 113 時? 2 分? 15 一つ目は11時53分 二つ目は2時15分 足すと14時8分 privateメソッドの呼び出し privateメソッドはクラス外部からは呼び出せない privateメソッドの呼び出し
Timeクラスの改良(1) 以前の考え方 別の考え方 例えば 時刻には時と分がある 時をメンバー変数hで表わし,分をメンバー変数mで表わす. mが60を越えたらhに繰り上げる 別の考え方 時刻は午前0時0分から経過した分を表わすメンバー変数minのみで表わされる 時はminを60で割った整数値である 時刻の分はminを60で割ったあまりである 例えば min = 795 時:min / 60 = 13 分:min % 60 = 15 min = 795 → 13時15分
利用者から見たTimeクラスの使用方法は何も変わっていない! class Time { private int min; public void Add(Time t) min = min + t.min; } public void Input() int h, m; Console.Write("時? "); h = int.Parse(Console.ReadLine()); Console.Write("分? "); m = int.Parse(Console.ReadLine()); min = 60 * h + m; public void Write() Console.Write("{0}時{1}分\n", min/60, min%60); 改良後のTimeクラス class Time { private int h; private int m; private void Adjust() while (m > 60) h = h + 1; m = m - 60; } public void Add(Time t) h = h + t.h; m = m + t.m; Adjust(); public void Input() Console.Write("時? "); h = int.Parse(Console.ReadLine()); Console.Write("分? "); m = int.Parse(Console.ReadLine()); public void Write() Console.Write("{0}時{1}分\n", h, m); メンバー変数はminだけ Adjust()メソッドは不要になった 足し算も簡単 時と分をminに変換 表示する時にminを時と分に変換 重要なこと 利用者から見たTimeクラスの使用方法は何も変わっていない!
こんなプログラムを作りたいけど,今のままのTimeクラスでは不可能! ユーザーの声 こんなプログラムを作りたいけど,今のままのTimeクラスでは不可能! class lecture5 { static void Main() Time t = new Time(); Console.WriteLine("今の時刻は?"); t.Input(); Console.Write("今の時刻は"); t.Write(); } 今の時刻は? 時? 13 分? 85 今の時刻は14時25分 今,何時かな? 13 分,何分かな? 85 今の時刻は14:25で~す. 残念! 新型Timeでは,h,mのフィールドは存在しない! Console.WriteLine("今の時刻は{0}:{1}で~す.", t.h, t.m);
オブジェクトのプロパティ プロパティとは? あたかもそういうフィールド(メンバー変数)がそのクラスに存在するかのように見せる仕組み! メソッド(関数)の定義みたいだけど,( )も引数も無い → プロパティの定義 プロパティとは? あたかもそういうフィールド(メンバー変数)がそのクラスに存在するかのように見せる仕組み! class Time { private int min; public void Add(Time t) { ・・・ } //省略 public void Input() public void Write() public int Minute get { return min%60; } set { int h = min/60; min = h * 60 + value; } public int Hour get { return min/60; } int m = min%60; min = value * 60 + m; 今,何時かな? 13 今,何分かな? 85 今の時刻は14:25で~す. class lecture5 { static void Main() Time t = new Time(); Console.Write("今,何時かな? "); t.Hour = int.Parse(Console.ReadLine()); Console.Write("今,何分かな? "); t.Minute = int.Parse(Console.ReadLine()); Console.WriteLine("今の時刻は{0}:{1}で~す", t.Hour, t.Minute); } オブジェクトから値を得るのはget Minuteプロパティの定義 Timeクラスのメンバー変数HourとMinuteみたいだけど・・・ だが,改良型Timeクラスにはメンバー変数はminだけ!! Hour, Minute → プロパティ オブジェクトに値を設定するのはset Hourプロパティの定義 代入文の右辺ではgetプロパティ 代入文の左辺ではsetプロパティ x = t.Hour; 右辺の値13はキーワードvalueに入る t.Hour = 13;
クラスのプログラム例 クラス名と同じメソッド名 → コンストラクタと呼ばれる特殊なメソッド インスタンス(クラス変数)を初期化する役割がある using System; class Complex { private double re; private double im; public Complex(double re, double im) { this.re = re; this.im = im; } public Complex() re = 1.0; im = 1.0; public double Real get { return re; } set { re = value; } public double Imag get { return im; } } クラス名と同じメソッド名 → コンストラクタと呼ばれる特殊なメソッド インスタンス(クラス変数)を初期化する役割がある コンストラクタの呼び出し z1は re=0.1 で im=10 に初期化される もう一つのコンストラクタ このコンストラクタには引数が無い この場合は引数が無い方のコンストラクタが呼び出される z2はre=1.0でim=1.0に初期化される class lecture5 { static void Main() { Complex z1 = new Complex(0.1, 10.0); Complex z2 = new Complex(); Console.WriteLine("z1 = ({0}, {1})", z1.Real, z1.Imag); Console.WriteLine("z2 = ({0}, {1})", z2.Real, z2.Imag); } }
このオブジェクトのメンバー変数を参照していることを示している コンストラクタの詳細とオーバーロード using System; class Complex { private double re; private double im; public Complex(double re, double im) { this.re = re; this.im = im; } public Complex() re = 1.0; im = 1.0; public double Real { ・・・ } //省略 public double Imag } コンストラクタのルール クラスと同じ名前 関数値は持たない(voidすらも指定できない) 通常はパブリックメソッド 関数名は同じだが,引数が異なる関数の定義 → オーバーロードと呼ぶ C言語ではコンパイルエラーになる C#言語では引数の数や組み合わせが異なる関数(メソッド)は異なった関数(メソッド)とみなされる 呼び出し時の引数の組み合わせからコンパイラが正しいメソッドを判断して呼び出す キーワードthis このオブジェクトのメンバー変数を参照していることを示している
staticメソッドなので頭にクラス名が必要 using System; class Complex { private double re; private double im; public Complex(double re, double im){ … } //略 public Complex(){ … }; //略 public double Real{ … }; //略 public double Imag{ … }; //略 public double Absolute { get { return Math.Sqrt(im*im + re*re); } } public void Add(Complex a) this.Real = this.Real + a.Real; this.Imag = this.Imag + a.Imag; public static Complex Add(Complex a, Complex b) Complex z = new Complex(); z.Real = a.Real+b.Real; z.Imag = a.Imag+b.Imag; return z; } パブリックプロパティ ここでは,単にプライベートフィールドのreやimの値をget/setするために定義 複素数の絶対値を得るAbsoluteプロパティ 但し,このプロパティにはsetプロパティの定義が無いので,代入文の左辺にはなれない z1.Absolute = 10.5 × class lecture5 { static void Main() { Complex z1 = new Complex(2.0, 10.0); Complex z2 = new Complex(-1.5, 10.2); Complex z3 = new Complex(); double x; x = z1.Absolute; z2.Add(z1); z3 = Complex.Add(z1, z2); } } ここではthisは無くても良いが,念のためつけておく. z2 ← z2 + z1 ここではthisを付けるべきメンバは存在しない. z3 ← z1 + z2 staticメソッドなので頭にクラス名が必要 staticキーワードは,インスタンス(クラス変数)ではなく,クラスに所属するメソッドであることを宣言している
静的メンバ(静的メソッド) 通常のメンバ(通常のメソッド) 静的メンバ(静的メソッド) 通常のメソッド 静的メソッド 静的メソッドの例 インスタンスの宣言 ↓ インスタンスがメモリ上に 作成される インスタンスのフィールド (メンバー変数)が using System; class Complex { // ・・・省略・・・・ public void Method1() { ・・・ } public static void Method2() } 通常のメンバ(通常のメソッド) 静的メンバ(静的メソッド) インスタンスのフィールド に関する処理 通常のメソッド インスタンス(クラス変数)に対して呼び出す インスタンスを宣言する前には呼び出せない 静的メソッド クラスに対して呼び出す インスタンスを宣言しなくても呼び出せる (インスタンスに対して呼び出すとエラーになる) class lecture5 { static void Main() { Complex z1 = new Complex(2.0, 10.0); Complex.Method1() z1.Method1(); Complex.Method2(); z1.Method2(); } } インスタンスのフィールドに 関係ない処理 静的メソッドの例 Console.WriteLine() → Consoleクラスの静的メソッド Math.Sqrt(double x) → Mathクラスの静的メソッド
演算子オーバーロード 演算子オーバーロード コンパイラは というソースを, という関数呼び出しと解釈 する z2←z2+z1 using System; class Complex { // ・・・省略・・・ public void Add(Complex a) { this.Real = this.Real + a.Real; this.Imag = this.Imag + a.Imag; } public static Complex Add(Complex a, Complex b) Complex z = new Complex(); z.Real = a.Real + b.Real; z.Imag = a.Imag + b.Imag; return z; public static Complex operator+(Complex a, Complex b) } class lecture5 { static void Main() { Complex z1 = new Complex(2.0, 10.0); Complex z2 = new Complex(-1.5, 10.2); Complex z3 = new Complex(); z2.Add(z1); z3 = Complex.Add(z1, z2); z3 = z1 + z2; } } z2←z2+z1 z3←z1+z2 演算子オーバーロード コンパイラは z3 = z1 + z2; というソースを, z3 = operator+(z1, z2) という関数呼び出しと解釈 する