細かい便利機能が多い。
成果物
情報源
コード
シグネチャ
public
/private
/protected
などabstract
/sealed
など- メソッド名
- 戻り値
- 引数
デリゲートは「戻り値」もシグネチャに含む。ただしオーバーロードするとき「戻り値」はシグネチャに含まない。
オーバーロード
class C { public void M() {} public void M(int a) {} }
class C { public void M() {} public int M() {} // error CS0111 }
戻り値だけが違うとオーバーロードできない。同じメソッドと判断されてエラー。
デリゲート
delegate void A(); //delegate int A(); // 変数名を他と同一にすると error CS0102 になる delegate int B();
呼出
引数と戻り値の有無パターン4つ
引数なし。戻り値なし。
class Main { public void Run() { A(); } public void A() => Console.WriteLine("A"); }
引数なし。戻り値あり。
class Main { public void Run() { int f = F(); Console.WriteLine($"{f}"); } public void F() => 100; }
引数あり。戻り値なし。
class Main { public void Run() { A(1); } public void A(int p) => Console.WriteLine($"A: p={p}"); }
引数あり。戻り値あり。
class Main { public void Run() { Console.WriteLine($"{F(2)}"); } public int F(int p) { Console.WriteLine($"F: p={p}"); return 101; } }
まとめると以下。
class Main { public void Run() { A(); int f = F(); Console.WriteLine($"{f}"); A(1); Console.WriteLine($"{F(2)}"); } public void A() => Console.WriteLine("A"); public int F() => 100; public void A(int p) => Console.WriteLine($"A: p={p}"); public int F(int p) { Console.WriteLine($"F: p={p}"); return 101; } }
引数パターン
- 位置引数
- 名前付き引数
位置引数
class Main { public void Run() { A(1, 2); } public void A(int p1, int p2) => Console.WriteLine($"A: p1={p1}, p2={p2}"); }
複数の位置引数があるときは、カンマ区切りで順に指定する。
名前付き引数
順序不定にできる。
class Main { public void Run() { P(p2:2, p1:1); } public void P(int p1, int p2) => Console.WriteLine($"P: p1={p1}, p2={p2}"); }
ただし名前付き引数は位置引数より後ろに書かねばならない。もし位置が一致しているならOK。
P(p2:2, 3); // error CS8323
P(p1:1, 2); // P: p1=1, p2=2
class Main { public void Run() { P(1, 2); // P: p1=1, p2=2 P(p2:2, p1:1); // P: p1=1, p2=2 P(1, p2:2); // P: p1=1, p2=2 P(p1:1, 2); // P: p1=1, p2=2 P(p2:2, 3); // error CS8323 N(1, 2); // N: p1=1, p2=2 N(p2:2, p1:1); // N: p1=1, p2=2 N(1, p2:2); // N: p1=1, p2=2 N(p1:1, 2); // N: p1=1, p2=2 N(p2:2, 3); // error CS8323 } public void P(int p1, int p2) => Console.WriteLine($"P: p1={p1}, p2={p2}"); public void N(int p1=0, int p2=0) => Console.WriteLine($"N: p1={p1}, p2={p2}"); }
オプション引数
class Main { public void Run() { // O(); // error CS7036 O(1); // O: p1=1, o1=0 // O(o1:2); // error CS7036 O(1, o1:2); // O: p1=1, o1=2 O(p1:1, o1:2); // O: p1=1, o1=2 O(o1:2, p1:1); // O: p1=1, o1=2 } public void O(int p1, int o1=0) => Console.WriteLine($"O: p1={p1}, o1={o1}"); }
オプション引数は省略できる。省略すると定義時の値となる。
だが位置引数は省略できない。
位置引数、オプション引数の順で定義する必要がある。
public void O2(int o=0, int p) => Console.WriteLine($"O: p={p}, o={o}"); // error CS1737
継承メソッドとoverride
メソッド
すべてのクラスはObject
クラスを継承する。Object
クラスに定義されてあるEquals
メソッドなどが使える。
class C {} var c = new C(); c.Equals(c);
override
すると以下。
class Main { class C { public override bool Equals(object obj) { Console.WriteLine("比較した"); return base.Equals(obj); } } public void Run() { var c = new C(); c.Equals(c); } }
値渡しと参照渡し
class Main { public void Run() { int i = 1; PassValue(i); Console.WriteLine($"i={i}"); int[] a = new int[1] { 1 }; PassReference(a); Console.WriteLine($"a[0]={a[0]}"); } private void PassValue(int v) { v = 2; } private void PassReference(int[] r) { r[0] = 2; } }
i=1 a[0]=2
関数内で引数に値をセットする。その後、呼出元で引数の値をみてみる。
値型は変化せず。参照型は変化した。
値型も参照として渡すことができる。
class Main { public void Run() { int i = 1; PassValue(ref i); Console.WriteLine($"i={i}"); } private void PassValue(ref int v) { v = 2; } }
i=2
ref
の他にもout
,in
がある。
class Main { public void Run() { int i = 1; PassValueR(ref i); Console.WriteLine($"i={i}"); int k; // PassValueR(out k); // error CS1620 PassValueO(out k); Console.WriteLine($"k={k}"); PassValueI(in i); } private void PassValueR(ref int v) { v = 2; } private void PassValueO(out int v) { v = 2; } // private void PassValueO(out int v) { } // error CS0177 private void PassValueI(in int v) { Console.WriteLine($"in v={v}"); } // private void PassValueI(in int v) { v = 2; } // error CS8331 }
out
は引数が事前に初期化されていなくてもOK。ref
は初期化必須。
in
は代入不可。ref
は代入可。out
は代入必須。
参照型にも使える。
可変長引数(params
)
class Main { public void Run() { Params(); Params(1); Params(1,3,5); } private void Params(params int[] args) { Console.WriteLine(String.Join(",", args)); } }
戻り値
class Main { public void Run() { Console.WriteLine($"{A()}"); Console.WriteLine($"{B()}"); Console.WriteLine($"{C()}"); } private int A() { return 1; } private int B() => 2; private (int,int) C() => (1,2); }
拡張メソッド
class Main { public void Run() { string s = "Hello Extension Methods"; Console.WriteLine($"{s.WordCount()}"); } } public static class MyExtensions { public static int WordCount(this String str) { return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length; } }
string
型にWordCount
メソッドを追加した。
非同期メソッド
class Main { public void Run() { M().Wait(); } private async Task M() { await Task.Delay(2000); Console.WriteLine($"非同期メソッド!"); } }
式
class Main { public void Run() { A(); C(); } private void A() => Console.WriteLine("A"); // private void B() => { } // error CS1525,CS1002,CS1519 private void C() { Console.WriteLine("C"); } }
1行だけならラムダ式でも書ける。
イテレータ
class Main { public void Run() { foreach (int i in A()) { Console.WriteLine($"i={i}"); } } private IEnumerable<int> A() { yield return 1; yield return 3; yield return 5; } }
対象環境
- 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