クラスでできることが多すぎる。学ぶのが大変。使いこなすのはもっと大変。でもこれは超大事な基本。
成果物
情報源
クラスの作成
Point
クラスを作成する。
0/Point.cs
class Point {}
フィールドを追加する。
class Point { public int x, y; }
フィールドを初期化するコンストラクタを追加する。
class Point { public int x, y; public Point(int x, int y) => (this.x, this.y) = (x, y); }
Point
クラスのインスタンスを生成する。
Program.cs
Point p = new Point(1, 2);
生成されたインスタンスは参照されなくなるとGC(ガーベジ・コレクション)によって自動的に削除される。そのタイミングは指示できない。
参照して表示すると以下。
Console.WriteLine($"{p.x}, {p.y}");
1, 2
クラスに含めることができるもの
アクセス修飾子の種類
- public
- internal
- protected
- protected internal
- private protected
- private
型パラメータ
ジェネリクス型。
1/Point.cs
public class Point<T> { public T x, y; public Point(T x, T y) => (this.x, this.y) = (x, y); }
宣言時に型を指定する。
Program.cs
var p = new Tours.Lesson1.Point<double>(1.1d, 2.2d); Console.WriteLine($"{p.x}, {p.y}");
1.1, 2.2
基底クラス
継承。基底クラスは継承元クラスのこと。派生クラスは継承先クラスのこと。
public class Point3D : Point { public int z; public Point3D(int x, int y, int z) : base(x,y) => this.z = z; }
base()
は親のコンストラクタを呼び出す略記。
コンストラクタ() : base() { ... }
呼び出すと以下。
var p = new Tours.Lesson2.Point3D(1,2,3); Console.WriteLine($"{p.x}, {p.y}, {p.z}");
1, 2, 3
フィールド
3/Color.cs
public class Color { public static readonly Color Black = new Color(0, 0, 0); public static readonly Color White = new Color(255, 255, 255); public static readonly Color Red = new Color(255, 0, 0); public static readonly Color Green = new Color(0, 255, 0); public static readonly Color Blue = new Color(0, 0, 255); private byte r, g, b; public Color(byte r, byte g, byte b) { this.r = r; this.g = g; this.b = b; } }
var c = new Tours.Lesson3.Color(16,32,64); //Console.WriteLine($"{c.r}, {c.g}, {c.b}"); // error CS0122 //Tours.Lesson3.Color.White = c; // error CS0198 Console.WriteLine($"{Tours.Lesson3.Color.White}");
Tours.Lesson3.Color
メソッド
種別 | 定義例 | 呼出例 | 概要 |
---|---|---|---|
静的メソッド | static void M() {} |
ClassName.M(); |
システムに1つだけあるメソッド |
インスタンスメソッド | void M() {} |
instance.M(); |
インスタンス個別にあるメソッド |
シグネチャ
シグネチャはクラス内で一意である必要がある。構成要素は以下。戻り値は含まれない。
- メソッド名
- 型パラメータの数
- パラメータの数、修飾子、型
以下はOK。名前が一意であるため。
int Method1() {} int Method2() {}
以下もOK。引数の数が一意のため。
int Method() {} int Method(int i) {}
居kははNG。メソッド名、パラメータが重複している。戻り値はシグネチャに含まれないため、異なっていても一意判定に関係なし。
int Method() {} string Method() {}
パラメータ(引数)
型
パラメータには変数を渡せる。変数には型がある。型は以下の2種類に大別できる。
型 | 渡し方 | 概要 |
---|---|---|
値型 | 値渡し | 元の値をコピーした値が渡される |
参照型 | 参照渡し | 元の値を参照するポインタが渡される |
in
/ref
/out
参照型にはオプションで以下の修飾子を付与できる。これらもシグネチャに含まれる。
修飾子 | 呼出前の初期化 | メソッド内代入 |
---|---|---|
in |
要 | ☓ |
ref |
要 | ○ |
out |
不 | ○ |
public static void M_in(in Point p) { p.x++; } // p = new Point(1,2); はエラー public static void M_ref(ref Point p) { p.x++; p = new Point(0,0); } // 既存の参照先がある。それを変更できる public static void M_out(out Point p) { p = new Point(0,0); p.x++; } // 既存の参照先は不要。メソッド内で先に代入必須。
var p1 = new Tours.Lesson4.Point(1,1); Console.WriteLine($"{p1.x}, {p1.y}"); Tours.Lesson4.Point.M_in(in p1); Console.WriteLine($"{p1.x}, {p1.y}"); Tours.Lesson4.Point.M_ref(ref p1); Console.WriteLine($"{p1.x}, {p1.y}"); Tours.Lesson4.Point.M_out(out p1); Console.WriteLine($"{p1.x}, {p1.y}"); Tours.Lesson4.Point p2; // 初期化せず //Tours.Lesson4.Point.M_in(in p2); // error CS0165 //Console.WriteLine($"{p1.x}, {p1.y}"); //Tours.Lesson4.Point.M_ref(ref p2); // error CS0165 //Console.WriteLine($"{p1.x}, {p1.y}"); Tours.Lesson4.Point.M_out(out p2); Console.WriteLine($"{p1.x}, {p1.y}");
1, 1 2, 1 0, 0 1, 0 1, 0
可変長引数
params
修飾子と配列を用いる。
void M(params int[] args) {}
コンストラクタの引数を可変長にする。N次元の点を生成する。
public class Points { public int[] Values { get; private set; } public Points(params int[] points) => this.Values = points; }
4次元の点を生成する。各軸の値を取得する。
var p = new Tours.Lesson5.Points(1,2,3,4); foreach (int x in p.Values) { Console.WriteLine($"{x}"); }
virtual
, override
, abstract
メソッド
修飾子 | 和名 | 概要 |
---|---|---|
virtual |
仮想 | 派生クラスにてoverride できる。しなくてもいい |
override |
上書き | 基底クラスの実装を上書きする |
abstract |
抽象 | 抽象クラス内でのみ宣言可。派生クラスにて実装を強いる。 |
class V { public virtual void V() { Console.WriteLine("virtual"); } } class O : V{ public override virtual void V() { Console.WriteLine("override"); } } abstract class A { abstract void A(); } class B : A { void A () { Console.WriteLine("abstract implement."); } }
var v = new Tours.Lesson6.V(); var o = new Tours.Lesson6.O(); //var a = new Tours.Lesson6.A(); // error CS0144 var b = new Tours.Lesson6.B(); v.M(); o.M(); b.M();
virtual override abstract implement.
オーバーロード
メソッドシグネチャが一意であれば、同一名のメソッドを定義できる。
class C { public void M() { Console.WriteLine("M()"); } public void M(int i) { Console.WriteLine("M(int i)"); } public void M(double d) { Console.WriteLine("M(double d)"); } }
var c = new Tours.Lesson7.C(); c.M(); c.M(0); c.M(0d);
M() M(int i) M(double d)
関数メンバ
関数メンバとは、クラス内におけるメンバのうち以下のような実行可能コードが含まれるメンバのこと。
var c = new Tours.Lesson8.C(); var d = new Tours.Lesson8.C(); c.Changed += (object sender, EventArgs e) => Console.WriteLine("Changed()"); Console.WriteLine($"{c.P1}"); Console.WriteLine($"{c.P2}"); Console.WriteLine($"{c["MyKey"]}"); Console.WriteLine($"{c == c}"); Console.WriteLine($"{c == d}");
class C { public C() => Console.WriteLine("コンストラクタ"); ~C() => Console.WriteLine("デストラクタ"); public string P1 => "プロパティ1"; public string P2 { get => "プロパティ2"; set => OnChanged(); } public string this[string key] { get => key; } public void M() => Console.WriteLine("M()"); public event EventHandler Changed; public static bool operator ==(C a, C b) => Equals(a, b); public static bool operator !=(C a, C b) => !Equals(a, b); protected virtual void OnChanged() => Changed?.Invoke(this, EventArgs.Empty); }
コンストラクタ コンストラクタ プロパティ1 プロパティ2 MyKey True False デストラクタ デストラクタ
リソースの破棄はデストラクタだけでなくIDisposable
を継承してDispose
を実装することでも行える。using(IDisposable d = new ?()){}
内で。両方で行うようにするのが確実かつ効率的。
C++のデストラクタというよりJavaのファイナライザに近い。
所感
大体知ってたから流せた。でも量が多くて大変。これだけ見たらクラスって大変そうなイメージ。けど、よく使うものから順に覚えればいい。プロパティ、メソッド、コンストラクタの3つだけで大体OKだと思う。
演算子、イベント、デストラクタ(ファイナライザ)は比較的特殊な場面でしか使わない。必要になってから覚えればいい。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13 ※
- bash 4.4.12(1)-release ※
- SQLite 3.29.0 ※
- C# dotnet 3.0.100 ※
$ uname -a Linux raspberrypi 4.19.42-v7+ #1218 SMP Tue May 14 00:48:17 BST 2019 armv7l GNU/Linux