●基本的なデータ型
- C# のデータ型は「値型」と「参照型」の二種類がある
- 値型はデータを直接変数 (メモリ) に保持する (数, 文字, 真偽値, 構造体など)
- 参照型はデータへの参照情報 (アドレス) を変数に保持する (文字列, 配列, オブジェクトなど)
- 数
- sbyte, short, int, long (8, 16, 32, 64 bit 符号付き整数)
- byte, ushort, uint, ulong (8, 16, 32, 64 bit 無符号整数)
- float (32 bit 浮動小数点数)
- double (64 bit 浮動小数点数)
- decimal (10 進小数)
- 文字 char, ' で囲む ('a', 'A' など)
- 真偽値 bool (true, false)
- 文字列 string, " で囲む (演算子 + で文字列の連結ができる)
- 配列
- 配列の定義
- データ型[] 変数名 = new データ型 [大きさ];
- データ型[] 変数名 = new データ型 [] {値1, ..., 値N};
- データ型[] 変数名 = {値1, ..., 値N};
- 要素のアクセスは角カッコ [ ] を使う
- 添字は 0 から始まる
- 配列の大きさは 変数名.Length で求めることができる
- 二次元配列の定義
- データ型[,] 変数名 = new データ型 [大きさ1, 大きさ2];
- データ型[,] 変数名 = new データ型 [,] {{値1, ..}, {値2, ...}};
- データ型[,] 変数名 = {{値1, ...}, {値2, ...}};
- 要素のアクセスは角カッコの中をカンマ ( , ) で区切る
- 多次元配列は角カッコの中を次元数の数だけカンマで区切る
- 多次元配列の場合、Length は配列全体の大きさになる
- 各次元の大きさはメソッド GetLenght() で求めることができる
- 配列の配列 (ジャグ配列) も定義できる
- データ型 [][] 変数名 = new データ型[大きさ][]
$ dotnet script
> int[] a = new int[10];
> a[0]
0
> a[9] = 1;
> a[9]
1
> int[,] b = {{1,2,3},{4,5,6},{7,8,9}};
> b[0,0]
1
> b[2,2]
9
> b[2,2] = 10;
> b[2,2]
10
> var c = new int[3, 4];
> c.Length
12
> c.GetLength(0)
3
> c.GetLength(1)
4
●基本的な演算子
- 算術演算子 (+, -, *, /, %)
- 比較演算子 (==, !=, <, >, <=, >=)
- 論理演算子 (!, &&, ||)
- ビット演算子 (~, &, |, ^, <<, >>)
- 代入演算子 (=, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=)
- インクリメント (++)
- デクリメント (--)
- 三項演算子 (test ? true節 : else節)
●基本的な制御構造
- if (test1) { then節; ... } else if (test2) { then節2; ... } else { else節; ... }
- switch(変数) { case 値1: 処理1; ...; break; ... default: 処理; ... }
- C# の switch はフォールスルーを禁止している
- ただし、case 1: case 2: のように連続して書くことはできる
- while (test) { 処理; ... }
- do { 処理; ... } while (test);
- for (初期化式; 条件式; 更新式) { 処理; ... }
- foreach(データ型 変数 in collection) { 処理; ... }
- 繰り返しの制御に break と continue が使える
●関数
- C# の場合、関数はメソッド (method) のことで、クラス (class) の中で定義する
- 関数のように使いたい場合は static を付ける (静的メソッド)
- static 返り値のデータ型 関数名(データ型 仮引数名, ...) { 処理; ...; return 返り値; }
- 返り値が無い場合はデータ型を void にする
- 関数呼び出しは 関数名(実引数, ...)
- 他のクラスから呼び出す場合は public も付ける
- この場合、関数呼び出しは クラス名.関数名(実引数, ...) になる
- 関数名は Pascal 形式 (各単語の頭文字を大文字にしてつなげる) で記述する
- C# の関数 (メソッド) は多重定義 (オーバーロード) が可能
- 引数名 (変数名) は Camel 形式 (先頭文字を小文字、つなげる単語の頭文字を大文字) で記述する
- 可変長引数の定義は データ型 関数名(..., params 配列型 仮引数名) { ... }
- 可変長引数に配列を渡すと、その配列を展開して関数に渡す
- オプション引数の定義は データ型 関数名(..., データ型 仮引数名 = 値) { ... }
- 関数を呼び出すとき、名前付き引数 (仮引数名: 値, ...) を使用できる
- 末尾再帰最適化はサポートされていないようだ
リスト : 階乗とフィボナッチ関数 (sample01/Program.cs)
using System;
class Test {
// 再帰
static long Fact(long n) {
return n == 0 ? 1 : n * Fact(n - 1);
}
// 繰り返し
static long Facti(long n) {
long a = 1;
for (long i = 2; i <= n; i++) a *= i;
return a;
}
// 再帰
static int Fibo(int n) {
if (n < 2) return n;
return Fibo(n - 2) + Fibo(n - 1);
}
// 繰り返し
static int Fiboi(int n) {
int a = 0, b = 1;
while (n-- > 0) {
int c = a + b;
a = b;
b = c;
}
return a;
}
static void Main() {
Console.WriteLine("{0}! = {1}", 10, Fact(10));
Console.WriteLine("{0}! = {1}", 15, Fact(15));
Console.WriteLine("{0}! = {1}", 20, Fact(20));
Console.WriteLine("{0}! = {1}", 10, Facti(10));
Console.WriteLine("{0}! = {1}", 15, Facti(15));
Console.WriteLine("{0}! = {1}", 20, Facti(20));
Console.WriteLine("Fibo({0}) = {1}", 10, Fibo(10));
Console.WriteLine("Fibo({0}) = {1}", 15, Fibo(15));
Console.WriteLine("Fibo({0}) = {1}", 20, Fibo(20));
Console.WriteLine("Fiboi({0}) = {1}", 10, Fiboi(10));
Console.WriteLine("Fiboi({0}) = {1}", 15, Fiboi(15));
Console.WriteLine("Fiboi({0}) = {1}", 20, Fiboi(20));
}
}
$ dotnet run --project sample01
10! = 3628800
15! = 1307674368000
20! = 2432902008176640000
10! = 3628800
15! = 1307674368000
20! = 2432902008176640000
Fibo(10) = 55
Fibo(15) = 610
Fibo(20) = 6765
Fiboi(10) = 55
Fiboi(15) = 610
Fiboi(20) = 6765
●変数
- 変数宣言は データ型 変数名; または データ型 変数名 = 初期値;
- 変数名は Camel 形式 (先頭文字を小文字、つなげる単語の頭文字を大文字) で記述する
- データ型に var を指定するとコンパイラがデータ型を決定する (型推論)
- 変数の有効範囲は宣言されているブロック { ... } の中
- ブロックが入れ子の場合、内側のブロックで外側のブロックと同名の変数を宣言することはできない
- C# の場合、グローバル変数は基本的に定義できない (代替の方法はある)
●クラス
- クラス定義は class クラス名 { ... }
- クラス内で宣言された変数を「フィールド (field)」とか「メンバ変数」という
- クラス内で定義された関数を「メソッド (method)」という
- メソッドに static を付けると「静的メソッド」になる
- 他のプログラミング言語では「クラスメソッド」と呼ばれることもある
- 静的メソッドは クラス名.メソッド名(...) で呼び出す
- フィールドに static を付けると「静的フィールド」になる
- 他のプログラミング言語では「クラス変数」と呼ばれることもある
- 静的フィールドは クラス名.フィールド名 でアクセスする
- フィールドやメソッドにはアクセス修飾子をつけることができる
- public, protected, internal, private
- 省略した場合は private
- インスタンスの生成は new クラス名() で行う
- このときクラスと同名のメソッド (コンストラクタ) が呼び出される
- アクセス修飾子 クラス名(データ型 仮引数, ...) { ... }
- コンストラクタは値を返さない (void はつけない)
- コンストラクタも多重定義できる
- static を付けると「静的コンストラクタ」になる
- 静的コンストラクタはクラスを初めて使うときに一度だけ呼び出される
- インスタンスを obj とすると、フィールドのアクセスは obj.field名, メソッドの呼び出しは obj.メソッド名(実引数, ...)
- メソッドの中で変数 this は自分自身 (インスタンス) を表す
- 不要になったインスタンスは GC (ガベージコレクション) により回収される
- このとき、デストラクタが呼び出される
- ~クラス名() { ... }
- 引数と返り値はない
- class の前に static を付けると「静的クラス」になる
- 静的クラスで定義できるのは静的なフィールドやメソッドだけ
- 静的クラスはインスタンスを生成できない
- using static クラス名; とすると、静的フィールドや静的メソッドの指定でクラス名を省略できる
リスト : 簡単な例題 (sample02/Program.cs)
using System;
class Point {
double x = 0.0, y = 0.0;
// コンストラクタ
public Point() { }
public Point(double x, double y) {
this.x = x;
this.y = y;
}
// メソッド
public double Distance(Point p) {
double dx = x - p.x;
double dy = y - p.y;
return Math.Sqrt(dx * dx + dy * dy);
}
}
class Point3D {
double x = 0.0, y = 0.0, z = 0.0;
// コンストラクタ
public Point3D() { }
public Point3D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
// メソッド
public double Distance(Point3D p) {
double dx = x - p.x;
double dy = y - p.y;
double dz = z - p.z;
return Math.Sqrt(dx * dx + dy * dy + dz * dz);
}
}
class Test {
static void Main() {
var p1 = new Point();
var p2 = new Point(1.0, 1.0);
Console.WriteLine("{0}", p1.Distance(p2));
var p3 = new Point3D();
var p4 = new Point3D(1.0, 1.0, 1.0);
Console.WriteLine("{0}", p3.Distance(p4));
}
}
$ dotnet run --project sample02
1.4142135623730951
1.7320508075688772
●継承
- C# のクラスは他のクラスのフィールドやメソッドを「継承」することができる
- 元になるクラスを「スーパークラス」、継承したクラスを「サブクラス」とか「派生クラス」という
- C# は単一継承なのでスーパークラスは一つだけ指定できる
- class クラス名 : スーパークラス { ... }
- スーパークラスの指定を省略すると暗黙のうちにクラス object を継承する
- 「インターフェース (interface)」はいくつでも継承できる
- コンストラクタは最上位のスーパークラスから順番に呼び出される
- このとき呼び出されるのは引数なしのコンストラクタ
- スーパークラスのコンストラクタは base を使って明示的に呼び出すこともできる
- public クラス名(引数, ...) : base(引数, ...) { ... }
- サブクラスでスーパークラスのメソッドと同名のメソッドを定義することができる
- これを「オーバーライド (over ride)」という
- C# の場合、オーバーライドする時は修飾子 new を付ける
- public new type MethodName(...) { ... }
- new をつけないとコンパイラが警告をだす
- オーバーライドしたメソッドからスーパークラスのメソッドを呼び出すときは base を使う
- base.MethodName(...); // スーパークラスの MethodName を呼び出す
- 継承を禁止する場合は class の前に修飾子 sealed を付ける
リスト : 継承の簡単なサンプル (sample03/Program.cs)
using System;
class Foo {
int x = 0, y = 0;
public Foo() {}
public Foo(int a, int b) {
x = a;
y = b;
}
// アクセスメソッド
public int GetX() { return x; }
public int GetY() { return y; }
public void SetX(int a) { x = a; }
public void SetY(int b) { y = b; }
// 合計値を求める
public int Sum() {
return x + y;
}
}
class Bar : Foo {
int z = 0;
public Bar() { }
public Bar(int a, int b, int c) : base(a, b) {
z = c;
}
// アクセスメソッド
public int GetZ() { return z; }
public void SetZ(int c) { z = c; }
// 合計値を求める
public new int Sum() {
return z + base.Sum();
}
}
class Test {
static void Main() {
var a = new Foo(1, 2);
var b = new Bar(10, 20, 30);
Console.WriteLine("{0}", a.Sum()); // 3 と表示
Console.WriteLine("{0}", b.Sum()); // 60 と表示
}
}
$ dotnet run --project sample03
3
60
●仮想関数とポリモーフィズム
- サブクラスのインスタンスはスーパークラスの変数に代入することができる
- これを「アップキャスト」という
- アップキャストした場合、スーパークラスと同じデータ型として扱われるため、「ポリモーフィズム」が機能しない
- ポリモーフィズムを機能させるには「仮想関数」を使う
- public virtual データ型 メソッド名(...) { ... }
- virtual を付けると仮想関数になる
- サブクラスでオーバーライドするメソッドは override を付ける
- public override データ型 メソッド名(...) { ... }
リスト : 仮想関数の簡単な例題 (sample04/Program.cs)
using System;
class Foo {
int x = 0, y = 0;
public Foo() {}
public Foo(int a, int b) {
x = a;
y = b;
}
// アクセスメソッド
public int GetX() { return x; }
public int GetY() { return y; }
public void SetX(int a) { x = a; }
public void SetY(int b) { y = b; }
// 合計値を求める
public virtual int Sum() {
return x + y;
}
}
class Bar : Foo {
int z = 0;
public Bar() { }
public Bar(int a, int b, int c) : base(a, b) {
z = c;
}
// アクセスメソッド
public int GetZ() { return z; }
public void SetZ(int c) { z = c; }
// 合計値を求める
public override int Sum() {
return z + base.Sum();
}
}
class Test {
static void Main() {
Foo a = new Foo(1, 2);
Foo b = new Bar(10, 20, 30); // アップキャスト
Console.WriteLine("{0}", a.Sum()); // 3 と表示
Console.WriteLine("{0}", b.Sum()); // 60 と表示
}
}
$ dotnet run --project sample04
3
60
●抽象クラスと抽象メソッド
リスト : 抽象クラスと抽象メソッドの簡単な例題 (sample05/Program.cs)
using System;
abstract class Figure {
public abstract string KindOf();
public abstract double Area();
public void Print(){
Console.WriteLine("{0}: area = {1}", KindOf(), Area());
}
}
// 三角形
class Triangle : Figure {
double altitude, base_line;
public Triangle(double a, double b){
altitude = a;
base_line = b;
}
public override string KindOf(){
return "Triangle";
}
public override double Area(){
return altitude * base_line / 2.0;
}
}
// 四角形
class Rectangle : Figure {
double width, height;
public Rectangle(double w, double h){
width = w;
height = h;
}
public override string KindOf(){
return "Rectangle";
}
public override double Area(){
return width * height;
}
}
// 円
class Circle : Figure {
double radius;
public Circle(double r){
radius = r;
}
public override string KindOf(){
return "Circle";
}
public override double Area(){
return radius * radius * Math.PI;
}
}
class Test {
static void Main() {
Triangle a = new Triangle(2.0, 2.0);
Rectangle b = new Rectangle(2.0, 2.0);
Circle c = new Circle(2.0);
a.Print();
b.Print();
c.Print();
Figure[] figTable = {
new Triangle(3.0, 3.0),
new Rectangle(3.0, 3.0),
new Circle(3.0),
};
foreach(Figure f in figTable) {
f.Print();
}
}
}
$ dotnet run --project sample05
Triangle: area = 2
Rectangle: area = 4
Circle: area = 12.566370614359172
Triangle: area = 4.5
Rectangle: area = 9
Circle: area = 28.274333882308138
●インターフェース
- インターフェース (interface) はメソッドの仕様 (宣言) だけを記述した抽象クラス
- interface インターフェース名 { メソッドの宣言; ... }
- 宣言されたメソッドは public abstract になる
- メソッドはインターフェースを継承したサブクラスで実装する
- class クラス名 : スーパークラス, インターフェース1, ... { メソッドの実装; ... }
- インターフェースはいくつでも継承することができる
- インターフェースもデータ型として使用できる (アップキャスト可能)
リスト : インターフェースの簡単な使用例 (sample06/Program.cs)
using System;
// インターフェースの定義
interface Figure {
string KindOf();
double Area();
void Print();
}
// 三角形
class Triangle : Figure {
double altitude, base_line;
public Triangle(double a, double b){
altitude = a;
base_line = b;
}
public string KindOf(){
return "Triangle";
}
public double Area(){
return altitude * base_line / 2.0;
}
public void Print(){
Console.WriteLine("{0}: area = {1}", KindOf(), Area());
}
}
// 四角形
class Rectangle : Figure {
double width, height;
public Rectangle(double w, double h){
width = w;
height = h;
}
public string KindOf(){
return "Rectangle";
}
public double Area(){
return width * height;
}
public void Print(){
Console.WriteLine("{0}: area = {1}", KindOf(), Area());
}
}
// 円
class Circle : Figure {
double radius;
public Circle(double r){
radius = r;
}
public string KindOf(){
return "Circle";
}
public double Area(){
return radius * radius * Math.PI;
}
public void Print(){
Console.WriteLine("{0}: area = {1}", KindOf(), Area());
}
}
class Test {
static void Main() {
Triangle a = new Triangle(2.0, 2.0);
Rectangle b = new Rectangle(2.0, 2.0);
Circle c = new Circle(2.0);
a.Print();
b.Print();
c.Print();
Figure[] figTable = {
new Triangle(3.0, 3.0),
new Rectangle(3.0, 3.0),
new Circle(3.0),
};
foreach(Figure f in figTable) {
f.Print();
}
}
}
$ dotnet run --project sample06
Triangle: area = 2
Rectangle: area = 4
Circle: area = 12.566370614359172
Triangle: area = 4.5
Rectangle: area = 9
Circle: area = 28.274333882308138
●プロパティ
リスト : プロパティの簡単な例題 (sample07/Program.cs)
using System;
class Foo {
public int X { set; get; }
public int Y { set; get; }
// 合計値を求める
public int Sum {
get { return X + Y; }
}
}
// getter だけ自動生成する
class Bar {
public Bar(int a, int b) {
X = a;
Y = b;
}
public int X { get; }
public int Y { get; }
// 合計値を求める
public int Sum {
get { return X + Y; }
}
}
class Test {
static void Main() {
var a = new Foo();
a.X = 10;
a.Y = 20;
Console.WriteLine("{0}", a.Sum); // 30
var b = new Bar(10, 20);
Console.WriteLine("{0}", b.Sum); // 30
}
}
$ dotnet run --project sample07
30
30
●ジェネリック
- ジェネリック (generics) はデータ型をパラメータ化する機能のこと
- 型パラメータ (型引数) は <T, U, V, ...> のように < > の中で指定する
- ジェネリックを使ってメソッドとクラスを定義できる
- データ型 メソッド名<T, ...>(仮引数, ...) where 制約条件 { ... }
- class クラス名<T, ...> where 制約条件 { ... }
- 制約条件の指定方法
- where T : struct (型 T は値型)
- where T : class (型 T は値型)
- where T : new() (型 T は引数なしのコンストラクタを持つ)
- where T : class_name (型 T はクラス class_name を継承する)
- where T : interface_name (型 T はインターフェース interface_name を実装している)
- データ型は クラス名<データ型, ...> になる
- インスタンスの生成は new クラス名<データ型, ...>(実引数, ...)
- メソッドの呼び出しは メソッド名<データ型, ...>(実引数, ...)
- 実引数から型変数のデータ型が特定できる場合、型変数の指定は省略できる
リスト : ジェネリックの簡単な使用例 (sample08/Program.cs)
using System;
class Foo<T> {
// T だけだとワーニングになるので T? を使う
// T? は null 許容型 "csharp01a.html#abc28" を参照
T? x;
public Foo() { x = default(T); }
public Foo(T n) { x = n; }
public T? Get() { return x; }
}
class Bar<T> where T : new() {
T y;
public Bar() { y = new T(); }
public Bar(T n) { y = n; }
public T Get() { return y; }
}
class Baz {
int z;
public Baz() { z = 123; }
public Baz(int n) { z = n; }
public int Get() { return z; }
}
class Test {
// 型変数の場合、比較演算子は使えないので、
// インターフェース IComparable を使う
static int IndexOf<T>(T[] buff, T x) where T : IComparable {
for (int i = 0; i < buff.Length; i++) {
if (x.CompareTo(buff[i]) == 0) return i;
}
return -1;
}
static void Main() {
var a = new Foo<int>();
var b = new Foo<double>(1.2345);
var c = new Bar<Baz>();
Console.WriteLine("{0}", a.Get()); // 0
Console.WriteLine("{0}", b.Get()); // 1.2345
Console.WriteLine("{0}", c.Get().Get()); // 123
int[] d = {1,2,3,4,5,6,7,8};
double[] e = {1.1, 2.2, 3.3, 4.4, 5.5};
Console.WriteLine("{0}", IndexOf(d, 5)); // 4
Console.WriteLine("{0}", IndexOf(d, 9)); // -1
Console.WriteLine("{0}", IndexOf(e, 4.4)); // 3
Console.WriteLine("{0}", IndexOf(e, 5.0)); // -1
}
}
$ dotnet run --project sample08
0
1.2345
123
4
-1
3
-1
●構造体
- 構造体はクラスのようにユーザーが定義するデータ型
- struct 構造体名 : インターフェース, ... { ... }
- クラスは参照型になるが構造体は値型になる
- 変数に代入する (または引数に渡す) ときは値がコピーされる
- 構造体は引数なしのコンストラクタやデストラクタを定義することができない
- 引数があるコンストラクタやメソッドは定義できる
- 構造体はクラスや他の構造体を継承することはできない
- 構造体を継承してサブクラスを定義することもできない
- インターフェースは継承することができる
- データの生成は new 構造体名() で行う
- 引数なしで構造体を生成した場合、値型のフィールドは 0 に、参照型のフィールドは null に初期化される
- ジェネリックも使用できる
リスト : 構造体とジェネリックの簡単な使用例 (sample09/Program.cs)
using System;
struct Point {
double x, y;
public Point(double a, double b) {
x = a;
y = b;
}
public double Distance(Point p) {
double dx = x - p.x;
double dy = y - p.y;
return Math.Sqrt(dx * dx + dy * dy);
}
}
struct Pair<T, U> {
T p;
U q;
public Pair(T a, U b) {
p = a;
q = b;
}
public T Fst() { return p; }
public U Snd() { return q; }
}
class Test {
static void Main() {
Point p1 = new Point();
Point p2 = new Point(1.0, 1.0);
Console.WriteLine("{0}", p1.Distance(p2)); // 1.4142135623731
var a = new Pair<string, int>("foo", 10);
var b = new Pair<string, double>("bar", 1.2345);
Console.WriteLine("{0}, {1}", a.Fst(), a.Snd()); // foo, 10
Console.WriteLine("{0}, {1}", b.Fst(), b.Snd()); // bar, 1.2345
}
}
$ dotnet run --project sample09
1.4142135623730951
foo, 10
bar, 1.2345
●デリゲートとラムダ式
- C# のデリゲート (delegate) はメソッドへの参照を表すデータ型を定義する
- delegate 返り値のデータ型 型名(データ型 仮引数, ...);
- C/C++の「関数へのポインタ」と似ている
- デリゲートを使ってメソッドを変数に代入したり関数の引数に渡すことができる
リスト : デリゲートの簡単な使用例 (sample10/Program.cs)
using System;
class Test {
// int を受け取って int を返すメソッドの型 IntFunc を定義
delegate int IntFunc(int x);
// マッピング (intFunc 型のメソッドを受け取る高階関数)
static int[] Map(IntFunc func, int[] xs) {
int[] ys = new int[xs.Length];
for (int i = 0; i < xs.Length; i++)
ys[i] = func(xs[i]);
return ys;
}
// 引数を二乗するメソッド
static int Square(int x) { return x * x; }
static void Main() {
int[] a = {1, 2, 3, 4, 5};
foreach(int x in Map(Square, a))
Console.Write("{0} ", x); // 1 4 9 16 25
Console.WriteLine("");
}
}
$ dotnet run --project sample10
1 4 9 16 25
- デリゲートはジェネリックを使って定義することができる
- delegate T FuncOne<T>(T x);
- static int[] Map(FuncOne<int> func, int[] xs) { ... }
- C# には汎用のデリゲート Func<T, U, ..., Result> と Action<T, U, ...> が用意されている
- Func は T, U, ... が仮引数の型、Result が返り値の型を表す
- static int[] Map(Func<int,int> func, int[] xs) { ... }
- 返り値が無い場合は Action を使う
- C# はデリゲートを使って匿名関数を定義できるが、今は「ラムダ式」を使ったほうが簡単
- ラムダ式の定義は (データ型 仮引数, ...) => { 処理; ...; return 値; }
- (仮引数, ...) => 式, 仮引数 => 式 とすることもできる (型推論が行われる)
リスト : ラムダ式の簡単な使用例 (sample11/Program.cs)
using System;
class Test {
// 配列のマッピング
static T[] Map<T>(Func<T, T> func, T[] xs) {
T[] ys = new T[xs.Length];
for (int i = 0; i < xs.Length; i++)
ys[i] = func(xs[i]);
return ys;
}
static void Main() {
int[] a = {1, 2, 3, 4, 5};
double[] b = {1.1, 2.2, 3.3, 4.4, 5.5};
foreach(int x in Map(n => n * n, a))
Console.Write("{0} ", x); // 1 4 9 16 25
Console.WriteLine("");
foreach(double x in Map(n => n * n, b))
Console.Write("{0} ", x); // 1.21 4.84 10.89 19.36 30.25
Console.WriteLine("");
}
}
dotnet run --project sample11
1 4 9 16 25
1.2100000000000002 4.840000000000001 10.889999999999999 19.360000000000003 30.25
- 「クロージャ (closure)」もサポートされている
$ dotnet script
> Func<int, Func<int, int>> make_adder = n => x => n + x;
> Func<int, int> add10 = make_adder(10);
> add10(1)
11
> add10(20)
30
- デリゲートには演算子 += を使って複数のメソッドを代入することができる
- これを「マルチキャストデリゲート」という
- メソッドは代入した順番に実行される
> Action<string> greeting = mes => Console.WriteLine("hello, {0}", mes);
> greeting("M.Hiroi")
hello, M.Hiroi
> greeting += mes => Console.WriteLine("good by, {0}", mes);
> greeting("M.Hiroi")
hello, M.Hiroi
good by, M.Hiroi
●例外処理
- 例外 (Exception) は throw 文で送出する
- throw 文で送出できるデータ型は System.Exception (例外クラス) を継承したクラス
- 例外の捕捉は try - chach - finally 文を使う
try {
...
} catch(例外クラス [引数]) {
...
} finally {
...
}
- catch 節には捕捉する例外クラスを指定する
- その引数には例外クラスのインスタンスがセットされる (引数は省略可)
- catch 節はいくつでも指定することができる
- finally 節は try 文の処理で例外が発生したかどうかにかかわらず、try 文の処理が終了するときに必ず実行される
$ dotnet script
> class Foo : Exception {};
> try { throw new Foo(); } catch(Foo) { Console.WriteLine("catch Foo"); }
catch Foo
> try { Console.WriteLine("foo"); } finally { Console.WriteLine("oops!")
foo
oops!
> try {throw new Foo();} catch(Foo) {Console.WriteLine("catch Foo");} finally
{Console.WriteLine("oops!");}
catch Foo
oops!
●可変長配列
- C# のライブラリには ArrayList と List<T> という可変長配列が用意されている
- ここではジェネリックの List<T> の基本的な操作を簡単に説明する
- List<T> は連結リストではない (LinkedList<T> が双方向リスト)
- ジェネリックコレクションを使うときは using System.Collections.Generic;
- コンストラクタ
- List<T>(); 空の配列を生成
- List<T>(collection); collection の要素を格納した配列を生成
- List<T>(size); 容量が size の配列を生成 (要素数は 0)
- プロパティ
- Capacity : 容量の取得と設定
- Count : 要素数の取得
- list[n] : n 番目の要素にアクセスする
- 基本的なメソッド
- Add(item) 配列の末尾に item を追加
- Clear() 配列を空にする
- Contains(item) 配列の中に item があれば true を返す
- Insert(n, item) 配列の n 番目に item を挿入
- RemoveAt(n) 配列の n 番目の要素を削除
- この他にも探索やソートなど便利なメソッドが多数用意されている
$ dotnet script
> var a = new List<int>();
> for (int i = 0; i < 10; i++) a.Add(i);
> a
List<int>(10) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
> a.Insert(0, -1)
> a
List<int>(11) { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
> a.RemoveAt(0)
> a
List<int>(10) { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
> a.Count
10
> a.Capacity
16
> a.RemoveAt(a.Count - 1);
> a
List<int>(9) { 0, 1, 2, 3, 4, 5, 6, 7, 8 }
●連想配列 (ハッシュ)
- C# のライブラリには HashTable と Dictionary<K, V> という連想配列 (ハッシュ) が用意されている
- ここではジェネリックの Dictionary<K, V> の基本的な操作を簡単に説明する
- ジェネリックコレクション を使うときは using System.Collections.Generic;
- コンストラクタ
- Dictionary<K, V>(); 空のハッシュを生成
- プロパティ
- Count : 要素数を取得する
- hash[key] : キー key の値を取得する (キーが見つからない場合はエラー)
- hash[key] = val; : キー key の値を val に更新する
- Keys : キーのコレクションを取得する
- Values : 値のコレクションを取得する
- 基本的なメソッド
- Add(key, val) key と val をハッシュに追加 (hash[key] = val でも OK)
- Clear() ハッシュを空にする
- ContainsKey(key) ハッシュの中にキー key があれば true を返す
- ContainsValue(val) ハッシュの中に値 val があれば true を返す
- Remove(key) ハッシュのキー key とその値を削除
- 削除できた場合は true, キーが見つからない場合は false を返す
$ dotnet script
> var a = new Dictionary<string, int>();
> a["foo"] = 10;
> a["baz"] = 20;
> a["bar"] = 30;
> a["oops"] = 40;
> a
Dictionary<string, int>(4) { { "foo", 10 }, { "baz", 20 }, { "bar", 30 },
{ "oops", 40 } }
> a.ContainsKey("foo")
true
> a.ContainsKey("Foo")
false
> a.Keys
Dictionary<string, int>.KeyCollection(4) { "foo", "baz", "bar", "oops" }
> a.Remove("foo")
true
> a
Dictionary<string, int>(3) { { "baz", 20 }, { "bar", 30 }, { "oops", 40 } }
> a.Remove("foo")
false
> a.Remove("Foo")
false