こんにちは。 pregum_foxです。
今回は前回の続きです。
残りの分を書いていきたいと思います。
また、JavaやKotlinの方が詳しいという方は以下のサイトをご覧になった方が 良いかと思います。
項目の順番は下記のLanguage tourに則っています。
目次です。
動作環境
以下の動作環境で確認しました。
言語 | 動作環境 |
---|---|
Dart | 2.4.0 |
C#/.Net Core | C#7.2 / .Net Core 2.1 |
演算子
算術演算子(2項演算子)
void main() { // 加算 assert(2 + 3 == 5); // 減算 assert(2 - 3 == -1); // 乗算 assert(2 * 3 == 6); // 除算 assert(5 / 2 == 2.5); // 戻り値はdouble // 除算(小数切り捨て) assert(5 ~/ 2 == 2); // 戻り値はint // 除算の余り(小数なし) assert((5 % 2) == 1 && (5 % 2) is int); // 除算の余り(小数あり) assert((5.5 % 2) == 1.5 && (5.5 % 2) is double); }
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { // 加算 Assert(2 + 3 == 5); // 減算 Assert(2 - 3 == -1); // 乗算 Assert(2 * 3 == 6); // 除算 (C#は割る数か割られる数を小数にしないと小数にならない。) Assert(5.0 / 2 == 2.5); // 除算(小数切り捨て) Assert(5 / 2 == 2); // 除算の余り(小数なし) Assert((5 % 2) == 1 && (5 % 2) is int); // 除算の余り(小数あり) Assert((5.5 % 2) == 1.5 && (5.5 % 2) is double); } }
算術演算子(単項演算子)
void main() { int a, b; a = 0; b = ++a; // bの代入前にaをインクリメントします。 assert(a == b); // 1 == 1 a = 0; b = a++; // bの代入後にaをインクリメントします。 assert(a != b); // 1 != 0 a = 0; b = --a; // bの代入前にaをデクリメントします。 assert(a == b); // -1 == -1 a = 0; b = a--; // bの代入後にaをデクリメントします。 assert(a != b); // -1 != 0 }
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { int a, b; a = 0; b = ++a; // bの代入前にaをインクリメントします。 Assert(a == b); // 1 == 1 a = 0; b = a++; // bの代入後にaをインクリメントします。 Assert(a != b); // 1 != 0 a = 0; b = --a; // bの代入前にaをデクリメントします。 Assert(a == b); // -1 == -1 a = 0; b = a--; // bの代入後にaをデクリメントします。 Assert(a != b); // -1 != 0 } }
等価演算子および関係演算子
void main() { // 等価演算子 assert(2 == 2); assert(null == null); assert(!(null == 0)); var a1 = A(3); var a1_2 = a1; var a2 = A(3); var a3 = A(4); // ==演算子は assert(a1 == a1_2); // identicalで参照が等価のオブジェクトであるか比較する assert(identical(a1, a1_2)); assert(a1 == a2); // a1とa2は値は等価だが参照は等価ではないためfalseを返る。 assert(!identical(a1, a2)); // a1とa3は値が等価でないためa1 != a3はtrueを返す。 assert(a1 != a3); assert(2 != 3); assert(3 > 2); assert(2 < 3); assert(3 >= 3); assert(2 <= 3); } class A { int x; A(this.x); // ==演算子をオーバーライドしてxの値で比較を行うよう変更している。 bool operator ==(o) => o is A && o.x == x; }
using System; using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { Assert(2 == 2); // クラスAはEquals(object obj)をオーバーライドしています。 var a1 = new A(3); var a1_2 = a1; var a2 = new A(3); var a3 = new A(4); // a1とa1_2は同一のインスタンスなので全て同一であり等価である。 Assert(a1 == a1_2); Assert(a1.Equals(a1_2)); Assert(object.ReferenceEquals(a1, a1_2)); // 同一のインスタンスではないのでa1 != a2はtrueを返す。 Assert(a1 != a2); // メンバのxフィールドが等価なのでtrueを返す。 Assert(a1.Equals(a2)); // 同一のインスタンスではないので下記はtrueを返す。 Assert(!object.ReferenceEquals(a1, a2)); // a1とa3は同一のインスタンスではなく // xフィールドの値も違う為、全て同一ではなく非等価である。 Assert(a1 != a3); Assert(!a1.Equals(a3)); Assert(!object.ReferenceEquals(a1, a3)); Assert(2 != 3); Assert(3 > 2); Assert(2 < 3); Assert(3 >= 3); Assert(2 <= 3); } public class A { public int x; public A(int x) { this.x = x; } public override bool Equals(object obj) { var @class = obj as A; return @class != null && x == @class.x; } public override int GetHashCode() { return 1249999374 + HashCode.Combine(this.x); } // もちろん==演算子をoverrideして内部でメンバ変数を比較しても上記の実装と // 同じように比較は可能 //public static bool operator ==(A obj, A obj2) //{ // if (object.ReferenceEquals(obj, obj2)) // { // return true; // } // if ((object)obj == null || (object)obj2 == null) // { // return false; // } // return obj.Equals(obj2); //} //public static bool operator !=(A obj, A obj2) //{ // return !(obj == obj2); //} } }
型検査演算子
void main() { Object person = Person(14); // if文の中でPersonクラスにキャストされている。 if (person is Person) { assert(person.age == 14); } (person as Person).age = 23; assert((person as Person).age == 23); Object dog = 'dog'; // Personクラスではないため、if文の中には入らない if (dog is Person){ assert(dog.age == 0); } // is!でdogがPersonクラスでないという演算子になる。 assert(dog is! Person); // print((dog as Person).age == 0); 例外発生 // as演算子でキャストを行った時点でエラー発生 // var res = dog as Person; } class Person { int age; Person(this.age); }
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { object person = new Person(14); // if文の中で変数pはPerson型として扱える。 if (person is Person p) { Assert(p.age == 14); } var person2 = person as Person; person2.age = 23; Assert(((Person)person).age == 23); object dog = "dog"; // Personクラスではないため、if文の中には入らない。 if (dog is Person d) { Assert(d.age == 0); } // C#には is! という演算子はない為、not演算子で代用 Assert(!(dog is Person)); // C#ではas演算子でキャストできない場合、nullを返す。 Assert((dog as Person)?.age == null); } } public class Person { public int age; public Person(int age) { this.age = age; } }
代入演算子
代入演算子の一覧は以下のサイトから確認できます。
void main() { num a = 5; assert(a == 5); int b; assert(b == null); b ??= 3; assert(b == 3); b ??= 7; assert(b == 3); // a op= b は a = a op bと同じ a += 4; assert(a == 9); a -= 3; assert(a == 6); a /= 3; assert(a == 2); a = 100; a %= 3; assert(a == 1); int c = 32; // 0b0010_0000 => 32 c >>= 1; // シフト演算 // 0b0001_0000 => 16 assert(c == 16); c ^= 8; // 排他的論理和 // 0b0001_1000 => 24 assert(c == 24); c *= 3; assert(c == 72); c ~/= 20.5; // 小数切り捨て assert(c == 3); c <<= 5; // シフト演算 // 2の5乗 * 3 => 96 assert(c == 96); c &= 0x002f; // 論理積 assert(c == 32); c |= 0x00ff; // 論理和 assert(c == 255); int m = -32; m >>= 1; // シフト演算 assert(m == -16); m = ~m; // bitごとに補数演算を行います。 // 0xffff_ffff_ffff_fff0 => -16 // ↓ bitごとに1/0を反転 // 0x0000_0000_0000_000f => 15 assert(m == 15); }
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { int a = 5; Assert(a == 5); // int?でnullを代入できるようにする。 int? b = null; Assert(b == null); // null合体代入演算子(??=)はC#にはないため??で代用する。 // ??= はC#8.0で追加される予定。 b = b ?? 3; Assert(b == 3); b = b ?? 7; Assert(b == 3); a += 4; Assert(a == 9); a -= 3; Assert(a == 6); a /= 3; Assert(a == 2); a = 100; a %= 3; Assert(a == 1); int c = 32; // 0b0010_0000 => 32 c >>= 1; Assert(c == 16); c ^= 8; Assert(c == 24); c *= 3; Assert(c == 72); // c ~/= はC#ではないため /= で代用 c /= (int)20.5; Assert(c == 3); c <<= 5; // シフト演算 Assert(c == 96); c &= 0x002f; // 論理積 Assert(c == 32); c |= 0x00ff; // 論理和 Assert(c == 255); int m = -32; m >>= 1; // シフト演算 Assert(m == -16); m = ~m; // bitごとに補数演算を行います。 // 0xffff_ffff_ffff_fff0 => -16 // ↓ bitごとに1/0を反転 // 0x0000_0000_0000_000f => 15 Assert(m == 15); } }
論理演算子
void main() { var True = true; var False = false; assert(!False); // 否定 assert(True && !False); // 論理積 assert(True || False); // 論理和 // echoFalse()のみ呼ばれる。 print('短絡評価テスト1'); assert(!(echoFalse() && echoTrue())); // echoTrue()のみ呼ばれる。 print('短絡評価テスト2'); assert(echoTrue() || !echoFalse()); // echoFalse()、echoTrue()両方呼ばれる。 print('短絡評価テスト3'); assert(!(echoFalse() & echoTrue())); // echoTrue()、echoFalse()両方呼ばれる。 print('短絡評価テスト4'); assert(echoTrue() | !echoFalse()); } // 実行結果 // 短絡評価テスト1 // false // 短絡評価テスト2 // true // 短絡評価テスト3 // false // true // 短絡評価テスト4 // true // false bool echoTrue() { print("true"); return true; } bool echoFalse() { print("false"); return false; }
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { // @を先頭につけることでキーワードを // 変数名として使用可能。 var @true = true; var @false = false; Assert(!@false); // 否定 Assert(@true && !@false); // 論理積 Assert(@true || @false); // 論理和 // EchoFalse()のみ呼ばれる。 WriteLine("短絡評価テスト1"); Assert(!(EchoFalse() && EchoTrue())); // EchoTrue()のみ呼ばれる。 WriteLine("短絡評価テスト2"); Assert(EchoTrue() || !EchoFalse()); // EchoFalse()、EchoTrue()両方呼ばれる。 WriteLine("短絡評価テスト3"); Assert(!(EchoFalse() & EchoTrue())); // EchoTrue()、EchoFalse()両方呼ばれる。 WriteLine("短絡評価テスト4"); Assert(EchoTrue() | !EchoFalse()); } // 実行結果 // 短絡評価テスト1 // false // 短絡評価テスト2 // true // 短絡評価テスト3 // false // true // 短絡評価テスト4 // true // false public static bool EchoTrue() { WriteLine("true"); return true; } public static bool EchoFalse() { WriteLine("false"); return false; } }
シフト演算子
void main() { var value = 0x22; assert((value << 4) == 0x220); assert((value >> 4) == 0x02); }
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { var value = 0x22; Assert((value << 4) == 0x220); Assert((value >> 4) == 0x02); } }
【Dartのみ】カスケード表記(..) / スプレッド演算子( ... / ...? )
スプレッド演算子はList、Map、Setリテラルに使用できます。
void main() { // カスケード演算子 // Flutterの方でWidgetのプロパティ設定用に使うと // 短くかけて、幸せになれるらしい。 var lis = <int>[1, 2, 3] ..add(4) ..add(5); // List<E>.generate(~)は、C#でいうEnumerable.Range(~)みたいなもの var lis2 = <num>[6.6, 7.7, 8.8] ..addAll(List<num>.generate(3, (i) => (i + 3) * 1.1 + 6.6)); var nullLis; var lis3 = [ ...?nullLis, 999, ...lis2, ]; assert(lis.length == 5); assert(lis3.length == 7); assert(lis2 is List<num>); print(lis3.runtimeType); // => List<dynamic> assert(lis is List<int>); }
その他の演算子
上記以外には
- () ・・・ 関数を呼び出す
- [] ・・・ リスト内の指定されたインデックスの値を参照
- . ・・・ 式のメンバを参照します。(foo.bar はfoo内のbarメンバを意味します。)
- ?. ・・・ 右辺値が式がnullだった場合、メンバを呼び出さずnullを返します。( foo?.barでfooがnullの場合、左辺値はnullになります。)
void main() { var lis = <int>[1, 2, 3]; // add()で()演算子が使用されている。 // ついでに.演算子でList<int>のaddメソッドが呼ばれている。 lis.add(4); // 下記のlis[2]で[]演算子を使用している。 assert(lis[3] == 4); List<int> nullList; // ?が付いていない呼び出しだとExceptionが発生する。 // ?.でnullListがnullなので何も起こらず次の処理へ移る。 nullList?.add(3); assert(nullList == null); }
using System.Collections.Generic; using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { var lis = new List<int> { 1, 2, 3 }; lis.Add(4); Assert(lis[3] == 4); List<int> nullList = null; nullList?.Add(3); Assert(nullList == null); } }
制御構文
if 文
void main() { var a = 3; if (a == 3) { print('process "if" path.'); } else if (a >= 2) { print('process "else-if" path.'); } else { print('process "else" path.'); } var b = 2; if (b == 3) { print('process "if" path.'); } else if (b >= 2) { print('process "else-if" path.'); } else { print('process "else" path.'); } var c = 1; if (c == 3) { print('process "if" path.'); } else if (c >= 2) { print('process "else-if" path.'); } else { print('process "else" path.'); } } // 実行結果 // process "if" path. // process "else-if" path. // process "else" path.
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { var a = 3; if (a == 3) { WriteLine(@"process ""if"" path."); } else if (a >= 2) { WriteLine(@"process ""else-if"" path."); } else { WriteLine(@"process ""else"" path."); } var b = 2; if (b == 3) { WriteLine(@"process ""if"" path."); } else if (b >= 2) { WriteLine(@"process ""else-if"" path."); } else { WriteLine(@"process ""else"" path."); } var c = 1; if (c == 3) { WriteLine(@"process ""if"" path."); } else if (c >= 2) { WriteLine(@"process ""else-if"" path."); } else { WriteLine(@"process ""else"" path."); } } } // 実行結果 // process "if" path. // process "else-if" path. // process "else" path.
for 文
void main() { var message = StringBuffer('Dart is fun'); for (var i = 0; i < 5; i++) { message.write('!'); } print(message); print('キャプチャテスト'); var callbacks = []; for (var i = 0; i < 2; i++) { // クロージャごとに値を保持している。 callbacks.add(() => print(i)); } callbacks.forEach((c) => c()); // for-inでC#のforeachのように反復処理が行うことができる。 print('for-inテスト'); var collection = [0, 1, 2]; for (var x in collection) { print(x); } } // 実行結果 // Dart is fun!!!!! // キャプチャテスト // 0 // 1 // for-inテスト // 0 // 1 // 2
using System; using System.Collections.Generic; using System.Text; using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { var message = new StringBuilder("C# is fun"); for (int i = 0; i < 5; i++) { message.Append("!"); } WriteLine(message); WriteLine("キャプチャテスト"); var callbacks = new List<Action>(); for (int i = 0; i < 2; i++) { // C#の場合はラムダ式内の変数はループ内で同じ変数扱いでキャプチャされるので // Dartと同じ動作にする場合、一度ローカルに代入するか // 別メソッドを作成する必要がある。 var t = i; callbacks.Add(() => WriteLine(t)); } callbacks.ForEach((c) => c()); // C#はforeachで反復処理を行う。 WriteLine("for-eachテスト"); var collection = new List<int> { 0, 1, 2 }; foreach (var x in collection) { WriteLine(x); } } } // 実行結果 // C# is fun!!!!! // キャプチャテスト // 0 // 1 // for-eachテスト // 0 // 1 // 2
while 文
do-while 文
break文 / continue文
void main() { print('while文'); // while文 var list = []; while (list.length < 5) { list.add(list.length); print(list); } print('do-while文'); // do-while文 list.clear(); do { list.add(list.length); print(list); } while (list.length < 5); print('break文'); // break文 while (true) { list.removeLast(); if (list.isEmpty) break; print(list); } print('continue文'); // continue文 list.clear(); while (list.length < 10) { list.add(list.length); if (list.length % 2 == 0) continue; print(list); } } // 実行結果 // [0, 1] // [0, 1, 2] // [0, 1, 2, 3] // [0, 1, 2, 3, 4] // do-while文 // [0] // [0, 1] // [0, 1, 2] // [0, 1, 2, 3] // [0, 1, 2, 3, 4] // break文 // [0, 1, 2, 3] // [0, 1, 2] // [0, 1] // [0] // continue文 // [0] // [0, 1, 2] // [0, 1, 2, 3, 4] // [0, 1, 2, 3, 4, 5, 6] // [0, 1, 2, 3, 4, 5, 6, 7, 8]
using System.Collections.Generic; using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { WriteLine("while文"); var list = new List<int>(); while (list.Count < 5) { list.Add(list.Count); WriteLine($"[{string.Join(", ", list)}]"); } WriteLine("do-while文"); list.Clear(); do { list.Add(list.Count); WriteLine($"[{string.Join(", ", list)}]"); } while (list.Count < 5); WriteLine("break文"); while (true) { list.RemoveAt(list.Count - 1); if (list.Count == 0) break; WriteLine($"[{string.Join(", ", list)}]"); } WriteLine("cointinue文"); list.Clear(); while (list.Count < 10) { list.Add(list.Count); if (list.Count % 2 == 0) continue; WriteLine($"[{string.Join(", ", list)}]"); } } } // 実行結果 // while文 // [0] // [0, 1] // [0, 1, 2] // [0, 1, 2, 3] // [0, 1, 2, 3, 4] // do-while文 // [0] // [0, 1] // [0, 1, 2] // [0, 1, 2, 3] // [0, 1, 2, 3, 4] // break文 // [0, 1, 2, 3] // [0, 1, 2] // [0, 1] // [0] // cointinue文 // [0] // [0, 1, 2] // [0, 1, 2, 3, 4] // [0, 1, 2, 3, 4, 5, 6] // [0, 1, 2, 3, 4, 5, 6, 7, 8]
switch文
void main() { // break文かreturn文が各caseの最後にないと // エラーが発生します。 Command command = Command.Open; switch (command) { case Command.Open: print('open'); break; case Command.Close: print('close'); break; case Command.Lock: print('lock'); break; case Command.UnLock: print('unlock'); break; case Command.None: default: print('none or default'); break; } print('フォールスルーテスト'); var none = Command.None; switchSample(none); } void switchSample(Command cmd) { switch (cmd) { case Command.None: print('execute none.'); // continue文とラベルを使用することで // フォールスルーを行うことができる。 continue fallThrough; fallThrough: case Command.Open: print('execute open.'); return; case Command.Close: print('execute close.'); return; case Command.Lock: print('execute lock'); return; case Command.UnLock: print('execute unlock'); return; } } enum Command { None, Open, Close, Lock, UnLock, } // 実行結果 // open // フォールスルーテスト // execute none. // execute open.
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { Command command = Command.Open; switch (command) { case Command.Open: WriteLine("open"); break; case Command.Close: WriteLine("close"); break; case Command.Lock: WriteLine("lock"); break; case Command.UnLock: WriteLine("unlock"); break; case Command.None: default: WriteLine("none or default"); break; } WriteLine("フォールスルーテスト"); var none = Command.None; SwitchSample(none); } static void SwitchSample(Command cmd) { switch (cmd) { // C#では処理後のフォールスルーができない。 case Command.None: WriteLine("execute none."); SwitchSample(Command.Open); break; case Command.Open: WriteLine("execute open."); return; case Command.Close: WriteLine("execute close."); return; case Command.Lock: return; case Command.UnLock: return; default: return; } } } enum Command { None, Open, Close, Lock, UnLock, } // 実行結果 // open // フォールスルーテスト // execute none. // execute open.
Assert
Assert(assert)メソッドは引数がtrueにならないと例外を発生させる メソッドです。
このメソッドはC#だとDebugビルドの時("DEBUG"シンボル設定時) のみ実行されます。 Dartでは、--enable-assertsオプションが指定された時実行されます。
void main() { // assert()メソッド var text; assert(text == null); // 第1引数がfalseになった場合、第2引数に設定したメッセージを表示します。 // 第2引数に設定しなかった場合、デフォルトのメッセージが表示されます。 var number = 100; assert(number < 100, 'numberは100未満で設定する必要があります。'); }
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { // Assert()メソッド string text = null; Assert(text == null); // 第1引数がfalseになった場合、第2引数に設定したメッセージを表示します。 // 第2引数に設定しなかった場合、デフォルトのメッセージが表示されます。 var number = 100; Assert(number < 100, "numberは100未満で設定する必要があります。"); } }
例外処理
void main() { try { throwException(); } catch (e, s) { // 再throwされないとここではキャッチしない。 // assert(e is OutOfMemoryError); // print(s); } } void throwException() { print('throw ex'); // 任意の型をthrowすることが可能 // 基本的にはError型またはException型を実装する型をthrowします。 try { throw FormatException(); } // on ~ catchで任意の型のみ通す on UnimplementedError catch (e) { print('catch unimplemented error.'); print(e); // rethrowで再throwが可能 rethrow; } // catch句は複数指定可能 on Exception catch (e, s) { print('catch exception'); print(e); print(s); } on Error catch (e, s) { print('catch error'); print(e); // 型がわからない場合はcatchですべての例外をキャッチ可能 // 第1引数は例外の変数で、第2引数はスタックトレースを取得可能 } catch (e, s) { print('catch unknown exception'); print(e); print(s); } finally { // 例外発生関係なく必ず通る print('finally'); } } // 実行結果 // throw ex // catch exception // FormatException // #0 throwException bin\main.dart:17 // #1 main bin\main.dart:3 // #2 _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19) // #3 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:172:12) // finally
using System; using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { try { ThrowException(); } catch (Exception e) { Assert(e is FormatException); WriteLine(e); } } static void ThrowException() { WriteLine("throw ex"); // Exception型を実装する型をthrowします。 try { throw new FormatException(); } catch (NotImplementedException e) { WriteLine("catch notimplemented exception."); WriteLine(e.ToString()); // C#の場合はthrowで再throwが可能。 // throw e; とすると、スタックトレースが上書きされる為注意 throw; } // catch句は複数指定可能 catch (Exception e) { // e.ToString(); でエラーメッセージとスタックトレースを出力することができる。 // エラーメッセージ : e.Message; // スタックトレース : e.StackTrace; // e.ToString() == e.Message + e.StackTrace; WriteLine("catch exception"); WriteLine(e.ToString()); } finally { WriteLine("finally"); } } } // 実行結果 // throw ex // 例外がスローされました: 'System.FormatException' (ConsoleApp3.dll の中) // catch exception // System.FormatException: One of the identified items was in an invalid format. // at Program.ThrowException() in C:\Users\XXX\Source\repos\ConsoleApp3\ConsoleApp3\Program.cs:line 25 // finally
クラス
コンストラクタ
import 'dart:math'; void main() { var p = Point(2, 2); // p.yメンバはfinalが指定されている為、再代入不可 // p.y = 3 // <= error! // コンパイル時定数を作成した場合、単一のインスタンスが生成される。 var constP = const Point(3, 3); final constP2 = const Point(3, 3); assert(identical(constP, constP2)); // constをつけるとコンパイル時定数になり // new(つけなくてもよい)をつけると非定数になる。 var a = const Point(4, 4); var b = new Point(4, 4); assert(!identical(a, b)); // aとbの参照は非等価である。 // 名前付きコンストラクタ var samp1 = const Sample.anonymous(4, 5); var samp2 = Sample.anonymous(4, 5); assert(samp1.first == 4); assert(!identical(samp1, samp2)); } class Sample { final first; final second; // コンストラクタ(thisをつけることで代入式を省略可能) // firstとsecondフィールドへの代入処理と同じ処理を行っている。 Sample(this.first, this.second); // 上記の処理は以下のコードと同じ // Sample(int f, int s) { // this.first = f; // this.seconf = s; // } // 名前付きコンストラクタ(オーバーロードの代わり) // constをつけることでコンパイル時定数 const Sample.anonymous(int first, int second) : this.first = first, this.second = second; @override String toString() { return 'first: $first, second: $second'; } }
using System; using System.Drawing; using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { var p = new Point(2, 2); p.Y = 3; // C#にはコンパイル時定数のインスタンスはない為 // とばします。 // C#ではnewは省略不可能 var samp1 = new Sample(4, 5); var samp2 = new Sample(4, 5); Assert(samp1.first == 4); Assert(!object.ReferenceEquals(samp1, samp2)); } } public class Sample { // フィールド版 public readonly int first; public readonly int second; // プロパティ版 //public int First { get;} // public int Second { get; } public Sample(int first, int second) { this.first = first; this.second = second; } // コンストラクタのオーバーロード public Sample(string hoge) { throw new NotImplementedException(); } public override string ToString() { return $"first: {first}, second: ${second}"; } }
コンストラクタ継承
void main() { var emp = Employee.fromJson(); // 1つ以上コンストラクタが定義されていた場合、 // デフォルトコンストラクタ(引数なし)は使用できない。 // var emp2 = Employee(); // <= error! print('cast'); if (emp is Person) { emp.firstName = 'Bob'; } (emp as Person).firstName = 'Bob'; print(emp.firstName); } // 実行結果 // in Person // in Employee // cast // Bob class Person { String firstName; Person.fromJson() { print('in Person'); } } class Employee extends Person { // 明示的に親クラスのコンストラクタを呼ぶ必要がある。 Employee.fromJson() : super.fromJson() { print('in Employee'); } }
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { var emp = new Employee(3); WriteLine("cast"); if (emp is Person e) { e.FirstName = "Bob"; } (emp as Person).FirstName = "Bob"; WriteLine(emp.FirstName); } } // 実行結果 // in Person with firstName // in Employee with base // in Employee with this // cast // Bob public class Person { public string FirstName { get; set; } public Person() { WriteLine("in Person"); } public Person(string firstName) { this.FirstName = firstName; WriteLine("in Person with firstName"); } } public class Employee : Person { // 下記の場合だとbaseをつけなくても良いが // 明示的に引数を渡したい場合はbase初期化子をつける必要がある。 public Employee() // : base()を付けた場合と同じ { WriteLine("in Employee"); } // 親クラスに明示的に引数を渡す場合の構文 public Employee(string firstName) : base(firstName) { WriteLine("in Employee with base"); } // thisをつけることで別のコンストラクタを呼ぶことができる。 public Employee(int x) : this(x.ToString()) { WriteLine("in Employee with this"); } }
初期化子リスト
void main() { var samp = Sample(17, 170); assert(samp.age == 17); assert(samp.tall == 170); } // 実行結果 // 17 // 170 class Sample { final int age; int tall; // finalが指定されているメンバを初期化する場合は // 初期化子リストを使用して初期化を行う。 Sample(int age, int tall) : assert(age != null), this.age = age { print(age); // final指定でないものはbody内に記述可能 this.tall = tall; print(tall); } // 以下の書き方だとコンパイルエラーが発生し実行できない。 // Sample(int age) // { // this.age = age; // } }
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { var samp = new Sample(17, 170); Assert(samp.age == 17); Assert(samp.tall == 170); } } // 実行結果 // 17 // 170 class Sample { public readonly int age; public int tall; public Sample(int age, int tall) { // C#はbody内に記述可能。 this.age = age; WriteLine(age); this.tall = tall; WriteLine(tall); } }
【Dartのみ】不変コンストラクタ / ファクトリコンストラクタ
void main() { print('不変コンストラクタテスト'); final samp = Sample(17, 170); const samp2 = Sample(17, 170); const samp3 = Sample(17, 170); assert(!identical(samp, samp2)); assert(identical(samp2, samp3)); // 不変コンストラクタなのでメンバの値は変更不可 // samp3.age = 3; // error! print('ファクトリコンストラクタテスト'); var table1 = TableSample('test1'); var table2 = TableSample('test2'); var table3 = TableSample('test1'); assert(identical(table1, table3)); assert(!identical(table1, table2)); } // 実行結果 // 不変コンストラクタテスト // ファクトリコンストラクタテスト // initialize test1 // initialize test2 // test1 contains table class Sample { final int age; final int tall; // 不変コンストラクタ const Sample(this.age, this.tall); } class TableSample{ static final Map<String, TableSample> _table = <String, TableSample>{}; final String key; // ファクトリコンストラクタ // ファクトリコンストラクタ内ではthisは使用不可 factory TableSample(String key){ if(_table.containsKey(key)){ print('$key contains table'); return _table[key]; } else { print('initialize $key'); final tableSample = TableSample._internal(key); _table[key] = tableSample; return tableSample; } } TableSample._internal(this.key); }
ゲッタ/セッタ
void main() { var rect = Rectangle(3, 4, 20, 15); assert(rect.left == 3); rect.right = 12; assert(rect.left == -8); } class Rectangle { int left, top, width, height; Rectangle(this.left, this.top, this.width, this.height); // ゲッタ int get right => left + width; // セッタ set right(int value) => left = value - width; // ゲッタ int get bottom => top + height; // セッタ set bottom(int value) => top = value - height; }
using static System.Diagnostics.Debug; class Program { static void Main(string[] args) { var rect = new Rectangle(3, 4, 20, 15); Assert(rect.left == 3); rect.Right = 12; Assert(rect.left == -8); } } public class Rectangle { public int left, top, width, height; public Rectangle(int left, int top, int width, int height) { this.left = left; this.top = top; this.width = width; this.height = height; } public int Right { get => left + width; // ゲッタ set => left = value - width; // セッタ } public int Bottom { get => top + height; // ゲッタ set => top = value - height; // セッタ } }
途中ですが
又途中ですが、長くなってきましたのでここで一旦区切りたいと思います。
25000文字程度にする予定でしたが、気づいたら30000文字を超えていました。。。
残っている大まかな項目は以下の通りです。(細かい部分は変わるかもしれません。)
- 列挙型
- ミックスイン
- クラス変数とクラスメソッド
- 【C のみ】拡張メソッド
- ジェネリクス
- ライブラリと可視性
- ライブラリの使用
- ライブラリの実装
- 非同期処理
- ジェネレータ
- 呼び出し可能クラス
- Isolate
- Typedefs
- メタデータ
- コメント
雑感
やっとクラスの説明がほとんど終わって、終わりが見え始めては来ていますが、
細かい項目が意外とあるので、もしかしたらあと2part必要になるかもしれません。
おそらくこのあたりから、私の理解が足りない部分が沢山出てくるかと思いますので、
指摘してもらえると助かります。
読んで頂き、ありがとうございました。