C# の基礎中の基礎 - 文法

 [Home] |  [Index] | 投稿日 2015/05/16


 

目次

 変数・定数

 

配列

配列は System.Array クラスのインスタンスである。

using System;

public class ArrayTest
{
  public static void Main()
  {
    // 配列の初期化 (要素は null)
    int[] a = new int[4];
    for (int i = 0; i < a.Length; i++)
    {
       a[i] = 10 * i;
    }
    PrintIntArray(a);
    
    // 配列の初期化 (要素も初期化)
    int[] b = {1,2,3,4};
    PrintIntArray(b);
    
    // 2次元配列の初期化 (要素は null)
    int[,] c = new int[2, 2];
    c[0, 0] = 10;
    c[0, 1] = 11;
    c[1, 0] = 12;
    c[1, 1] = 13;
    Console.Write(c[0, 0]);
    Console.Write(" ");
    Console.Write(c[0, 1]);
    Console.Write(" ");
    Console.Write(c[1, 0]);
    Console.Write(" ");
    Console.WriteLine(c[1, 1]);
    
    // 2次元配列の初期化 (要素も初期化)
    int[,] d = {{5,6},{7,8}};
    Console.Write(d[0, 0]);
    Console.Write(" ");
    Console.Write(d[0, 1]);
    Console.Write(" ");
    Console.Write(d[1, 0]);
    Console.Write(" ");
    Console.WriteLine(d[1, 1]);
    
    // 配列の配列として2次元配列を表現するとき
    int[][] e = new int[2][];
    e[0] = new int[2];
    e[1] = new int[2];
    e[0][0] = 20;
    e[0][1] = 21;
    e[1][0] = 22;
    e[1][1] = 23;
    Console.Write(e[0][0]);
    Console.Write(" ");
    Console.Write(e[0][1]);
    Console.Write(" ");
    Console.Write(e[1][0]);
    Console.Write(" ");
    Console.WriteLine(e[1][1]);
  }
  
  public static void PrintIntArray(int[] a)
  {
    for (int i = 0; i < a.Length; i++)
    {
      Console.Write(a[i]);
      Console.Write(" ");
    }
    Console.WriteLine();
  }
}

[Top] [SubIndex]

キーワード(予約語)を変数名として使うには

推奨されていませんが可能です。ただし、予約語そのものを使うことはできません。予約語の前に @ を付けると変数とみなされます。コンテキストキーワード (*1) は @ を付けずに変数名として使える場合があります。

(参考) VB.NET では [ ] で囲むと予約語を変数とみなします。

using System;

public class EscapedName
{
  public static void Main()
  {
    var @if = 0;
    var @else = 1;
    var @switch = 2;
    var value = 3;
    var set = 4;
    var get = 5;
    
    Console.WriteLine(@if);
    Console.WriteLine(@else);
    Console.WriteLine(@switch);
    Console.WriteLine(value);
    Console.WriteLine(set);
    Console.WriteLine(get);
  }
}

*1 コンテキストキーワードとはプログラムの文脈内で特別な意味を持つキーワードです。例えば、value はプロパティの set メソッド内だけで特別な意味を持ちます。コンテキストキーワードには以下のようなものがあります (C# 4.0 の場合)。

@ はメソッド名とかにも使えるようです。

using System;

public class IIf
{
  // Ruby や Perl の "文 if 条件" のようなメソッド
  public static void @if(bool b, Action act)
  {
     if (b) { act(); }
  }
  
  public static void Main()
  {
     for (int i = 0; i < 5; i++)
     {
        @if(i % 2 == 0, ()=>{ Console.WriteLine(i); });
     }
  }
}

[Top] [SubIndex]


const と readonly の違い

const は一切変更できない定数、readonly はコンストラクタで初期化した後は変更できない変数である。どちらもコンパイル時にチェックされ、規則違反のときはコンパイルエラーとなる。

using System;

public class Readonly
{
  public static void Main()
  {
    var o = new Class1(true);
    Console.WriteLine(o);
  }
}

class Class1
{
  const int Zero = 0;
  readonly int n = 0;
  
  public Class1(bool b)
  {
    if (b)
    {
      n = 1;
    }
  }
  
  public override string ToString()
  {
    return Zero.ToString() + "," + n.ToString();
  }
}

[Top] [SubIndex]


変数の最大値・最小値を求めるには

整数、浮動小数点数などの最大値や最小値を求めるにはその型(クラス)における MaxValue や MinValue を参照すればよい。

using System;

public class MaxMin
{
  public static void Main()
  {
    Console.WriteLine(SByte.MinValue);
    Console.WriteLine(SByte.MaxValue);
    Console.WriteLine(Byte.MinValue);
    Console.WriteLine(Byte.MaxValue);
    Console.WriteLine(Int16.MinValue);
    Console.WriteLine(Int16.MaxValue);
    Console.WriteLine(UInt16.MinValue);
    Console.WriteLine(UInt16.MaxValue);
    Console.WriteLine(Int32.MinValue);
    Console.WriteLine(Int32.MaxValue);
    Console.WriteLine(UInt32.MinValue);
    Console.WriteLine(UInt32.MaxValue);
    Console.WriteLine(Int64.MinValue);
    Console.WriteLine(Int64.MaxValue);
    Console.WriteLine(UInt64.MinValue);
    Console.WriteLine(UInt64.MaxValue);
    Console.WriteLine(Single.MinValue);
    Console.WriteLine(Single.MaxValue);
    Console.WriteLine(Double.MinValue);
    Console.WriteLine(Double.MaxValue);
    Console.WriteLine(Decimal.MinValue);
    Console.WriteLine(Decimal.MaxValue);
  }
}

実行例

PS C:\> .\bin\MaxMin.exe
-128
127
0
255
-32768
32767
0
65535
-2147483648
2147483647
0
4294967295
-9223372036854775808
9223372036854775807
0
18446744073709551615
-3.402823E+38
3.402823E+38
-1.79769313486232E+308
1.79769313486232E+308
-79228162514264337593543950335
79228162514264337593543950335

[Top] [SubIndex]


checked と unchecked (オーバーフローのチェック)

checked キーワードは、整数型の算術演算および変換に対してオーバーフロー チェックを明示的に有効にするために使用する。checked は下の例のように { } ブロックの接頭辞として使用する。そして、このブロック内でのみチェックが有効である。

using System;

public class Checked
{
  public static void Main()
  {
    checked
    {
      int i = Int32.MaxValue;
      i++;
      Console.WriteLine(i);
    }
  }
}

実行例

PS C:\> .\Checked.exe

ハンドルされていない例外: System.OverflowException: 算術演算の結果オーバーフローが発生しました。
   場所 Checked.Main()

checked ブロックがない場合の実行例 (オーバーフローは発生せず最小値になる)

PS C:\> .\Checked.exe
-2147483648

unchecked は checked と異なりオーバーフローが発生する可能性があるコードをコンパイルしてもコンパイルエラーにならない。

using System;

public class UnChecked
{
  public static void Main()
  {
    int i = unchecked(Int32.MaxValue + 1);  // 7行目
    Console.WriteLine(i);
    int j = Int32.MaxValue + 1;  // 9行目
    Console.WriteLine(j);
  }
}

7行目は unchecked が付いているのでコンパイルエラーにならない。9行目は unchecked がないのでコンパイルエラーになる。

PS C:\> csc /out:.\bin/UnChecked.exe UnChecked.cs
Microsoft (R) Visual C# Compiler Version 4.0.30319.34209
for Microsoft (R) .NET Framework 4.5
Copyright (C) Microsoft Corporation. All rights reserved.

UnChecked.cs(9,13): error CS0220: この操作はチェック モードでコンパイルしたときにオーバーフローします。

[Top] [SubIndex]


ヒアドキュメント

ヒアドキュメントとは書いた通りの内容を持つ文字列であり、改行やエスケープ文字 \ が含まれていてもよい。C# では @"..." でヒアドキュメントを定義できる。

using System;

public class HereDoc
{
  public static void Main()
  {
    var str = @"
ヒアドキュメントとは
 エスケープ文字 \ や改行
を含んでもよい文字列である。
";
    Console.WriteLine(str);
}
}

PS C:\> .\bin\HereDoc.exe

ヒアドキュメントとは
 エスケープ文字 \ や改行
を含んでもよい文字列である。

[Top] [SubIndex]


null 許容型

null 許容型は、System.Nullable 構造体のインスタンスで、T? (例:int?) のように書くことができる。これは型 T 以外に null を代入できる。

using System;

public class NullableTest
{
  public static void Main()
  {
    // Nullable を T? と書くことができる。
    int? n = null;
    
    // HasValue プロパティで null かどうかを判別できる。
    Console.WriteLine(n.HasValue ? ((int)n).ToString() : "null");  // キャストすると値を取得できる。
    n = 100;
    Console.WriteLine(n.HasValue ? n.Value.ToString() : "null");  // Value プロパティでも値を取得できる。
    
    // object 型に代入した場合
    object obj = n;
    Console.WriteLine(obj.GetType());  // GetType() メソッドで Value プロパティの型がわかる。
  }
}

実行例

PS C:\> bin\NullableTest
null
100
System.Int32

[Top] [SubIndex]


暗黙に型指定される変数

var キーワードを使って宣言した変数は「型推測」により適切な型にある。

using System;

public class ImplicitTypeTest
{
  public static void Main()
  {
     var i = 100;  // System.Int32
     Console.WriteLine(i.GetType());
     var ii = (long)100;  // System.Int64
     Console.WriteLine(ii.GetType());
     var a = new [] { 1, 2, 3, 4 };  // System.Int32 の配列
     for (int j = 0; j < a.Length; j++)
     {
        Console.Write(a[j]);
        Console.Write(" ");
     }
     Console.WriteLine();
     var b = new { x = 0.5, y = 1.0 };  // 匿名型
     Console.WriteLine(b.x);
     Console.WriteLine(b.y);
  }
}


[Top] [SubIndex]

 

 ディレクティブ

 

C# にはどんなディレクティブがあるか

ディレクティブとは #if のように # で始まる文である。これらはコンパイルの前にプリプロセッサが処理を行う。

ディレクティブ 説明
#if .. [#else .. [#elif] .. [[#elif ..] .. #endif 指定されたシンボルが定義されている場合のみコンパイルされる。(#define 参照)
&& や || が使用可能。
#define シンボルを定義する。(コンパイルスイッチでも定義可能)
#undef シンボルの定義をやめる。
#warning コードの特定の位置でレベル 1 警告を表示する。
#error 特定の位置でエラーを表示する。
#line コンパイラから見たソースの行番号を変更する。(これによりエラーや警告により表示される行番号が変更できる)
#region .. #endregion コードのブロックを定義する。(これにより Visual Studio ではコードを折りたたんで表示できる)
#pragma name arg [.. arg] コンパイラに特殊な命令を与える。
#pragma warning 特定の警告を有効または無効にする。
#pragma checksum ASP.NET でページのデバッグに使用するソース ファイルのチェックサムを生成する。

[Top] [SubIndex]


DEBUG シンボルの使い方 (#if .. #endif)

DEBUG シンボルは Visual Studio においてデバッグモードでコンパイルしたときに定義されるシンボルである。Visual Studio ではコンソールアプリケーションが終了するとウィンドウがすぐ閉じてしまうので次のように DEBUG シンボルと #if .. #endif ディレクティブを使って、デバッグ時にウィンドウが閉じないようにすると便利である。

#if DEBUG
  Console.Readkey();
#endif

#if ブロック内でメッセージを表示するには #error や #warning を使用する。

#if DEBUG
#error デバッグモードです。
...
#endif

[Top] [SubIndex]


#define, #undef の使い方

#define, #undef は #if .. #endif で使用するシンボルを定義(あるいは未定義)するのに使う。これによりコードのブロックをまとめて無効(あるいは有効)にすることができる。1つのソースをわずかに機能が違う複数のアプリケーションで共用にするなどの場合などに使用できる。

#define VER3011
...
#if VER3011
  ...
#else
  ...
#endif
...
#undef VER3011

(参考) /define コンパイラオプションでもシンボル定義が可能。

[Top] [SubIndex]


#line の使い方

#line ディレクティブを使うとコンパイル時のエラー発生位置やファイル名を変更できる。

using System;

public class LineDirective
{
  public static void Main()
  {
    // エラー位置、ファイル名を変更する。
#line 1000 "Test.cs"
    int i;
    i = (int)"1000";
// エラー位置だけを変更する。
#line 2000
    i = (int)"2000";
    // デフォルトに戻す。
#line default
    i = (int)"1000";
    // 後続の行をデバッガーから隠す。(Step over が起きる)
    // 行番号は影響を受けない。
#line hidden
    i = (int)"1000";
}
}

このソースをコンパイルするとエラーメッセージは下のようになる。

PS C:\> csc /nologo /out:.\bin\LineDirective.exe .\LineDirective.cs
Test.cs(1001,9): error CS0030: 型 'string' を型 'int' に変換できません。
Test.cs(2000,9): error CS0030: 型 'string' を型 'int' に変換できません。
LineDirective.cs(16,9): error CS0030: 型 'string' を型 'int' に変換できません。
LineDirective.cs(20,9): error CS0030: 型 'string' を型 'int' に変換できません。
PS C:\>

[Top] [SubIndex]


#region .. #endregion の使い方

Visual Studio で #region を使うとソースを折りたたんで表示できる。

DataSet ds = new DataSet();
#region データベースに接続する。
string connString = @"Data Source=C:\test3\test.db";
SQLiteConnection conn = new SQLiteConnection(connString);
conn.Open();
#endregion
SQLiteCommand command = new SQLiteCommand();
command.Connection = conn;

Visual Studio でソースを折りたたんで表示した様子


[Top] [SubIndex]


 

 演算子

 

クラスに演算子を追加するには

operator キーワードを使うと、演算子もメソッドのように扱うことができる。これによりクラスに演算子を追加できる。

using System;

// Main
public class Operator
{
  public static void Main()
  {
    var a = new Class1(5);
    var b = new Class1(7);
    var y = a + b;
    var z = -a;
    Console.WriteLine(y);
    Console.WriteLine(z);
    var lv = (long)b;
    Console.WriteLine(lv);
    Console.WriteLine(lv.GetType());
  }
}

// 
class Class1
{
  public int val;
  
  public Class1()
  {
    val = 0;
  }
  
  public Class1(int n)
  {
    val = n;
  }
  
  // 二項演算子の例
  public static Class1 operator + (Class1 a, Class1 b)
  {
    return new Class1(a.val + b.val);
  }
  
  // 単項演算子の例
  public static Class1 operator - (Class1 a)
  {
    return new Class1(-a.val);
  }
  
  // キャスト演算子の例
  public static explicit operator long (Class1 a)
  {
    return Convert.ToInt64(a.val);
  }
  
  public override string ToString()
  {
    return val.ToString();
  }
}

実行例

PS C:\> .\bin\Operator.exe
12
-5
7
System.Int64

[Top] [SubIndex]


implicit (暗黙のユーザー定義型変換演算子を宣言)

変数の型を変換したい場合、キャストがしばしば使われる。implicit を使って実装したキャスト演算子は (型) を省略してその変換部分のコードを書くことができる。

(参考) explicit もある。「クラスに演算子を追加するには」参照。

using System;

// Main
public class Implicit
{
  public static void Main()
  {
    var o = new Int32A(10);
    int n = o;  // 暗黙的に整数に変換
    Console.WriteLine(n);
  }
}

class Int32A
{
  public int val;  // Int32 は sealed なので継承できないため、こんなふうにしている。
  
  public Int32A(int n)
  {
    val = n;
  }
  
  // キャスト演算子に implicit を付けて暗黙的な変換を行えるようにする。
  public static implicit operator int(Int32A x)
  {
    return x.val;
  }
}

実行例

PS C:\> .\bin\Implicit.exe
10

[Top] [SubIndex]


?: 演算子

?: 演算子は3項演算子と呼ばれ第一の式の値が真の場合、第二の式の値、そうでない場合、第三の式の値を取る。

string s = (i > 0) ? "番号は正です。" : "番号は0または負です。";

[Top] [SubIndex]


?? 演算子

?? 演算子は null 許容型、例えば int? に対して使用する。変数の値が null だった場合は、代替値を結果として返す。

using System;

public class NullCoales
{
  public static void Main()
  {
    int? n = 0;
    int n2 = n ?? int.MinValue;  // n は 非null なので n2 は n になる。
    Console.WriteLine(n2);
    n = null;
    n2 = n ?? int.MinValue;  // n は null なので n2 は null の代わりに整数の最小値になる。
    Console.WriteLine(n2);
  }
}

実行例 C:\> .\bin\NullCoales.exe <0>

[Top] [SubIndex]


 

 Enum

Enum クラス

列挙型 (enum 型) は、基になる型が整数型の一連の名前付き定数である。enum は Enum クラスは enum 型を作るための基本クラスである。したがって、enum は Enum クラスから派生しているのでそのメソッドやプロパティを利用可能である。

using System;

public class EnumTest
{
  public enum E1 { A, B, C, D };
  
  public static void Main()
  {
    E1 e;
    e = E1.A;  // シンボルAを設定する。
    Console.WriteLine(e);
    
    // E1の2番目(先頭は0)のシンボルを得る。
    Console.WriteLine(Enum.GetName(typeof(E1), 2));
    
    // E1 の値一覧を得る。
    foreach (var x in Enum.GetValues(typeof(E1)))
    {
       Console.WriteLine(x);
    }
  }
}

実行例

PS C:\> .\bin\EnumTest.exe
A
D
C
A
B
C
D

[Top]


Flags 属性付きの列挙型

Flags 属性を付加すると整数のビットにシンボルの値を割り付けることができる。これにより、& や | 演算子などでビット演算を行ってフラグが立っているかどうかを判別できる。

using System;

public class EnumTest2
{
  [Flags] public enum E2
  {
    A = 0,
    B = 1,
    C = 2,
    D = 4
  };
  
  public static void Main()
  {
    E2 e = E2.B | E2.D;
    
    var x = e & E2.A;
    if (x != 0)
    {
      Console.WriteLine("Includes A");
    }
    
    x = e & E2.B;
    if (x != 0)
    {
      Console.WriteLine("Includes B");
    }
    x = e & E2.C;
    if (x != 0)
    {
      Console.WriteLine("Includes C");
    }
    x = e & E2.D;
    if (x != 0)
    {
      Console.WriteLine("Includes D");
    }
  }
}

実行例

PS C:\workspace\dotnet\C#\Syntax> .\bin\EnumTest2
Includes B
Includes D

[Top]


 

 クラス

 

メソッドをオーバーライドするには

基本的なこと

using System;
using System.IO;
using System.Text;

public class VirtualTest
{
  public const string Filename = @"C:\temp\data.csv";
  
  public static void Main(String[] args)
  {
    var o2 = new Class2(10, 15, 20, 24);
    o2.SaveAs(Filename);
    var o = new Class2();
    o.Load(Filename);
    Console.WriteLine(o);
  }
}

// 基本クラス
//   Load, SaveAs メソッドはバイナリーデータをロード、保存する。
class Class1
{
  // 派生クラスからアクセスできるように protected を付ける。
  protected byte[] data;
  
  // コンストラクタ
  public Class1(params byte[] data)
  {
    this.data = (byte[])data.Clone();
  }
  
  // オーバーライドできるように virtual を付けたメソッド Load
  public virtual void Load(string filename)
  {
     this.data = File.ReadAllBytes(filename);
  }
  
  // オーバーライドできるように virtual を付けたメソッド SaveAs
  public virtual void SaveAs(string filename)
  {
     File.WriteAllBytes(filename, this.data);
  }
  
  // すべてのクラスは System.Object から派生しているので、ToString() をクラスに追加するには override が必要。
  public override string ToString()
  {
     var sb = new StringBuilder();
     for (var i = 0; i < this.data.Length; i++)
     {
        sb.Append(String.Format("{0,2:x2}", this.data[i]));
        if (i < this.data.Length - 1)
          sb.Append(" ");
     }
     return sb.ToString();
  }
}

// 派生クラス
//   Load, SaveAs メソッドはClass1のものをオーバーライドしてデータをテキストでロード、   保存するように変更。
class Class2 : Class1
{
  // デフォルトのコンストラクタ
  public Class2()
  {
  }
  
  // 基底クラス Class1 と同じ形式のコンストラクタだが override は不要。
  // Class1 のコンストラクタを呼び出すには ":" の後に base([引数]) を付ける。
  public Class2(params byte[] data) : base(data)
  {
  }
  
  // オーバーライドされた Load
  public override void Load(string filename)
  {
     var text = File.ReadAllText(filename);
     var da = text.Split(' ');
     base.data = new byte[da.Length];
     for (var i = 0; i < da.Length; i++)
     {
       base.data[i] = Convert.ToByte(da[i], 16);
     }
  }
  
  // オーバーライドされた SaveAs
  public override void SaveAs(string filename)
  {
     File.WriteAllText(filename, ToString());
  }
}

[Top][SubIndex]


メソッドをオーバーロードするには

「オーバーロード」と「オーバーライド」は言葉が似ているが機能は全く異なる。オーバーライドは「再定義」で、オーバーロードは「多重定義」である。

C#では「オーバーロード機能」を使って同じ名前のメソッドやコンストラクタを複数定義できる。これらのメソッドやコンストラクタは、パラメータの数や型で区別できるようにする。(戻り値が違っていても多重定義にはならない)

オーバーロードはオーバーライドと違い、特別なキーワードを付ける必要はない。

using System;
using System.Text;

public class OverloadTest
{
  public static void Main(String[] args)
  {
     var o = new Class1(5, 10, 15, 20, 25);
     Console.WriteLine(o);  // o.ToString() と同じ。
     Console.WriteLine(o.ToString(','));
  }
}

class Class1
{
  public byte[] data;
  
  // コンストラクタ
  public Class1()
  {
  }
  
  // オーバーロードされたコンストラクタ
  public Class1(params byte[] p)
  {
    data = p;
  }
  
  // data の長さを返すプロパティ
  public int Length
  {
    get { return data.Length; }
  }
  
  // 基本クラスの System.Object で定義されているため override が必要。
  public override string ToString()
  {
     return this.ToString(' ');
  }
  
  // オーバーロードされた ToString()
  public string ToString(char separator)
  {
     var sb = new StringBuilder();
     
     for (int i = 0; i < data.Length; i++)
     {
        sb.Append(String.Format("{0, 2:x2}", data[i]));
        if (i < data.Length - 1)
          sb.Append(separator);
     }
     
     return sb.ToString();
  }
}

[Top][SubIndex]


基本クラスのメソッドを隠すには

基本クラスのメソッドと同じ名前のメソッドを(別のものとして)再定義するには、new キーワードを使って基本クラスのメソッドを隠す。

(参考) VB.NET では Shadows キーワードを使う。

using System;

public class ShadowsTest
{

  public static void Main(String[] args)
  {
     var o = new Class1();
     Console.WriteLine(o.ToString());
  }
}

class Class1
{
  private readonly string name = "";
  
  public Class1()
  {
     name = "Class1";
  }
  
  // Class1 は System.Object の派生クラスなので ToString() と同じ名前のメソッドを作るためには new が必要になる。
  public new string ToString()
  {
    return name;
  }
}

[Top][SubIndex]


基本クラスのメソッドを呼び出すには

基本クラスのメソッドはオーバーライドしていない場合は、そのまま呼び出すことができる。あるいは base を使って明示的に呼び出すこともできる。オーバーライドしてある場合は、base を使って基本クラスのメソッドであることを明示する。

using System;

public class BaseTest
{
  public static void Main(String[] args)
  {
     var o = new Class2("class2");
     Console.WriteLine(o.ToString());
     Console.WriteLine(o.getValue());
  }
}


// 基底クラス
class Class1
{
  protected object value;
  
  public Class1(object v)
  {
    this.value = v;
  }
  
  // Class1 の getValue() は value を返す。
  public virtual object getValue()
  {
     return this.value;
  }
}

// 派生クラス
class Class2 : Class1
{
  public Class2(string name) : base(name)  // 基底クラスのコンストラクタを呼び出す。
  {
  }
  
  // Class2 の getValue() はこのオブジェクトのハッシュを返す。
  public override object getValue()
  {
    return GetHashCode();
  }
  
  // 文字列表現
  public override string ToString()
  {
    var v = base.getValue();  // Class1 の getValue() を呼び出す。
    return v.ToString();
  }
}

[Top][SubIndex]


基本クラスのコンストラクタを呼び出すには

基本クラスのコンストラクタを呼び出すには、base キーワードを使って基本クラスのコンストラクタを呼び出す。

// 派生クラス
class Class2 : Class1
{
  public Class2(string name) : base(name)  // 基底クラスのコンストラクタを呼び出す。
  {
  }
// ...
}

[Top][SubIndex]


メソッドで可変数のパラメータを定義するには

params キーワードを使ってパラメータを定義すると可変個数のパラメータを受け取れるようになる。

using System;

public class ParamsTest
{
  public static void Main(String[] args)
  {
     var vector = new Class1(1, 2, 3, 4, 5, 6, 7, 8);
     var b = vector.Slice(2, 6);
     for (int i = 0; i < b.Length; i++)
     {
        Console.WriteLine("{0:d3}", b[i]);
     }
  }
}

class Class1
{
  byte[] data;
  
  // params キーワードを使って可変個数のパラメータを受け取れるようにしたコンストラクタ   
  public Class1(params byte[] d)
  {
    data = d;
  }
  
  // data の一部を切り取るメソッド
  public byte[] Slice(int start, int end)
  {
    var slice = new byte[end - start + 1];
    for (int i = start; i <= end; i++)
    {
       slice[i - start] = data[i];
    }
    return slice;
  }
}

[Top][SubIndex]


メソッドでパラメータの既定値を定義するには

メソッドのパラメータリストで = 演算子でリテラルを設定するとそのパラメータを省略したとき、その値が使用される。パラメータは右から順に使わなければならず、もし、オーバーロードされたメソッドがある場合、そのメソッドと呼び出し時に明確に区別がつくようにしなければならない。

using System;

public class DefParam
{
  public static void Main()
  {
    test("Mikan", "Ringo");  // 第3パラメータ banana を省略して呼び出す。
  }
  
  // パラメータ banana に省略時に使用する既定値を設定している。
  public static void test(string orange, string apple, string banana="ばなな")
  {
     Console.WriteLine(orange);
     Console.WriteLine(apple);
     Console.WriteLine(banana);
  }
}

[Top][SubIndex]


メソッド呼び出し時に名前を付けてパラメータを与えるには

メソッドを呼び出すときにパラメータの名前と値をペアにして呼び出すことができる。この場合、パラメータの順番はメソッドの宣言と同じである必要はない。

using System;

public class NamedParam
{
  public static void Main()
  {
    test(orange:"Mikan", apple:"Ringo", banana:"Banana");  // 名前を付けてパラメータを指定。
  }
  
  // テスト用メソッド
  public static void test(string orange, string apple, string banana)
  {
     Console.WriteLine(orange);
     Console.WriteLine(apple);
     Console.WriteLine(banana);
  }
}

[Top][SubIndex]


クラスの文字列表現を定義するには

すべてのクラスは System.Object から派生しているので、Syste.Object.ToString() が使用できる。このメソッドをオーバーライドすることによりそのオブジェクトの独自の文字列表現を返すようになる。

using System;

public class ToStringTest
{
  public static void Main()
  {
     var product = new Product(9801, "PC-9801", 220000);
     Console.WriteLine(product);  // Product.ToString() が自動的に使用される。
  }
}

class Product
{
  int code;
  string name;
  int price;
  
  public Product(int code, string name, int price)
  {
     this.code = code;
     this.name = name;
     this.price = price;
  }
  
  // System.Object.ToString() をオーバーライドする。
  public override string ToString()
  {
     return String.Format("{0,5:d5} {1} {2:c}", code, name, price);
  }
}

実行例

PS C:\> .\bin\ToStringTest.exe
09801 PC-9801 \220,000

[Top][SubIndex]


オブジェクトを永続化するには

オブジェクトが破棄される直前に状態を保存し、次回、そのオブジェクトを構築したとき前の状態を復元することができる(オブジェクトの永続化 あるいはシリアライズと呼ぶ)。永続化にはバイナリー永続化と XML 永続化がある。

バイナリー永続化の例を以下に示す。

using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;

public class SerializeTest
{
  public const string Filename = @"c:\temp\serialize.bin";
  
  public static void Main()
  {
    var obj = new Class1();
    
    if (File.Exists(Filename))
    {
       // 前の状態を回復する。
       var streamIn = File.OpenRead(Filename);
       var deserializer = new BinaryFormatter();
       obj = (Class1)deserializer.Deserialize(streamIn);
       streamIn.Close();
    }
    
    // obj の状態を変更
    obj.Name = "Class1:" + DateTime.Now.ToShortDateString();
    obj.Inc();
    Console.WriteLine(obj);
    
    // 状態を保存
    var streamOut = File.Create(Filename);
    var serializer = new BinaryFormatter();
    serializer.Serialize(streamOut, obj);
    streamOut.Close();
  }
}

// バイナリーシリアライズ対象であることをマーク
[Serializable()]
class Class1
{
  // A は const なのでシリアライズ対象から外す。
  [field: NonSerialized()]
  public const char A = 'A';
  public int version = 1;
  private string name = "Class1";
  
  public Class1()
  {
  }
  
  public string Name
  {
    get { return name; }
    set { name = value; }
  }
  
  public void Inc()
  {
     version++;
  }
  
  public override string ToString()
  {
    var sb = new StringBuilder();
    sb.Append(A);
    sb.Append(' ');
    sb.Append(Name);
    sb.Append(' ');
    sb.Append(version);
    return sb.ToString();
  }
}

XML での永続化の例を以下に示す。

using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

public class XmlSerializerTest
{
  public const string Filename = @"c:\temp\serialize.xml";
  
  public static void Main()
  {
    var serializer = new XmlSerializer(typeof(Class1));;
    var obj = new Class1();
    
    if (File.Exists(Filename))
    {
       // 前の状態を回復する。
       var streamIn = new StreamReader(Filename);
       obj = (Class1)serializer.Deserialize(streamIn);
       streamIn.Close();
    }
    
    // obj の状態を変更
    obj.Name = "Class1:" + DateTime.Now.ToShortDateString();
    obj.Inc();
    Console.WriteLine(obj);
    
    // XMLファイルに状態を保存する。
    var streamOut = new StreamWriter(Filename);
    var writer = new XmlTextWriter(streamOut);
    serializer.Serialize(writer, obj);
    writer.Close();
  }
}

// シリアライズ対象のクラス
public class Class1
{
  public const char A = 'A';
  public int version = 1;
  private string name = "Class1";
  
  public Class1()
  {
  }
  
  public string Name
  {
    get { return name; }
    set { name = value; }
  }
  
  public void Inc()
  {
     version++;
  }
  
  public override string ToString()
  {
    var sb = new StringBuilder();
    sb.Append(A);
    sb.Append(' ');
    sb.Append(Name);
    sb.Append(' ');
    sb.Append(version);
    return sb.ToString();
  }
}

[Top][SubIndex]


オブジェクトを複製するには

要点

コピーコンストラクタとは次の例のようなものである。

using System;

public class CopyObjectTest
{
  public static void Main()
  {
     var o1 = new Class1();
     o1.Inc();
     o1.Inc();
     o1.Inc();
     var o2 = new Class1(o1);
     Console.WriteLine(o2.count);
  }
}


public class Class1
{
  public int count;
  
  // デフォルトのコンストラクタ
  public Class1()
  {
     count = 0;
  }
  
  // コピーコンストラクタ
  public Class1(Class1 c)
  {
     this.count = c.count;
  }
  
  public void Inc()
  {
     count++;
  }
}

Object.MemberwiseClone の使用例を下に示す。MemberwiseClone() は protected メソッドなので外部から直接コールできないことに注意。

using System;

public class MemberCloneTest
{
  public static void Main()
  {
     var o1 = new Class1();
     o1.Inc();
     o1.Inc();
     o1.Inc();
     var o2 = o1.Clone();
     Console.WriteLine(o2.count);
  }
}


public class Class1
{
  public int count;
  
  public Class1()
  {
     count = 0;
  }
  
  public void Inc()
  {
     count++;
  }
  
  // MemberwriseClone() を使用するためのメソッドを用意する。
  public Class1 Clone()
  {
     // MemberwriseClone() は protected のため直接外部からコールできない。
     return (Class1)MemberwiseClone();
  }
}

[Top][SubIndex]


オブジェクトを比較するには

要点

以下の例は比較演算子や比較メソッドの動作を表している。

using System;

public class MemberCloneTest
{
  public static void Main()
  {
     var o1 = new Class1();
     o1.Inc();
     o1.Inc();
     o1.Inc();
     var o2 = o1.Clone();
     Console.WriteLine(o2.count);  // 3
     
     // 参照型のオブジェクトの場合
     Console.WriteLine(o1 == o2);  // False
     Console.WriteLine(o1.Equals(o2));  // False
     Console.WriteLine(Object.ReferenceEquals(o1, o2));  // False
     o1.count = 3;
     Console.WriteLine(o1.Equals(o2));  // False
     
     // 値型のオブジェクトの場合
     int i = 0;
     int j = 0;
     Console.WriteLine(i == j);  // True
     Console.WriteLine(i.Equals(j));  // True
     Console.WriteLine(Object.ReferenceEquals(i, j));  // False
  }
}


public class Class1
{
  public int count;
  
  public Class1()
  {
     count = 0;
  }
  
  public void Inc()
  {
     count++;
  }
  
  // MemberwriseClone() を使用するためのメソッドを用意する。
  public Class1 Clone()
  {
     // MemberwriseClone() は protected のため直接外部からコールできない。
     return (Class1)MemberwiseClone();
  }
}

[Top][SubIndex]


オブジェクトを並べ替えるには

任意のクラスのコレクションを並べ替えるには IComparable インタフェースを実装すればよい。

using System;

public class SortTest
{
  public static void Main()
  {
     Class1[] arr = new Class1[4];
     arr[0] = new Class1("123");
     arr[1] = new Class1("a");
     arr[2] = new Class1("aaaaaa");
     arr[3] = new Class1("a C");
     Array.Sort(arr);
     foreach (var x in arr)
     {
        Console.WriteLine(x.Name);
     }
  }
}

// IComparable を実装するとこのクラスを比較できるようになる。
class Class1 : IComparable
{
  public string Name { get; set; }
  public int length;
  
  public Class1(string name)
  {
     Name = name;
     length = name.Length;
  }
  
  // この場合、降順ソートになる。昇順にしたいときは、(this.length - s.length) を返せばよい。
  public int CompareTo(object obj)
  {
     Class1 s = (Class1)obj;
     return s.length - this.length;  // Name の長さで比較する。
  }
}

[Top][SubIndex]


複数のクラスやインタフェースを継承するには

C# では特別なキーワードは必要なくて、:の後に継承元のクラスやインタフェースを並べて書くだけである。

using System;

public class MultiInheritTest
{
  public static void Main()
  {
     Class1[] arr = new Class1[4];
     arr[0] = new Class1("12");
     arr[1] = new Class1("xfffff");
     arr[2] = new Class1("a");
     arr[3] = (Class1)arr[2].Clone();
     Array.Sort(arr);
     
     foreach (var x in arr)
     {
        Console.WriteLine(x.Name);
     }
  }
}

class Class1 : IComparable, ICloneable
{
  public string Name { get; set; }
  public int length;
  
  public Class1(string name)
  {
     Name = name;
     length = name.Length;
  }
  
  // IComparable の実装
  public int CompareTo(object obj)
  {
     Class1 s = (Class1)obj;
     return s.length - this.length;  // Name の長さで比較する。
  }
  
  // ICloneable の実装
  public object Clone()
  {
    return new Class1(this.Name);
  }
}

[Top][SubIndex]


抽象クラスとは

抽象クラスとは abstract キーワードで宣言されたメソッドを含むクラスで、直接インスタンスすることはできず、必ず他のクラスで継承して使うクラスである。

using System;

public class AbstractTest
{
  public static void Main()
  {
    var o = new Class1("hello");  // コンパイルエラーになる。
  }
}

// abstract を付けると抽象クラスとみなされる。
abstract class Class1
{
  public string Name { get; set; }
  
  public Class1(string name)
  {
     this.Name = name;
  }
  
  // メソッド本体がない抽象メソッド (継承先でオーバーライドする)
  public abstract void Capitalize();
}

[Top][SubIndex]


継承不可クラスとは

継承不可クラスとは sealed キーワードで宣言されたクラスで、他のクラスの基底クラスとして使うことができないクラスである。

using System;

public class AbstractTest
{
  public static void Main()
  {
    var o = new Class1("hello");
    o.Capitalize();
    Console.WriteLine(o.Name);
  }
}

// sealed を付けると継承できなくなる。
sealed class Class1
{
  public string Name { get; set; }
  
  public Class1(string name)
  {
     this.Name = name;
  }
  
  // メソッド本体がない抽象メソッド
  public void Capitalize()
  {
     Name = Name.ToUpper();
  }
}

// Class1 を継承する
class Class2 : Class1   // コンパイルエラーになる。
{
}

[Top][SubIndex]


internal とは

internal なメンバーあるいはクラスは、同じアセンブリのファイル内でのみアクセスできる。クラスの宣言で修飾子を省略すると internal が指定されたのと同じになる。クラスのメンバーは既定は private なので internal を省略すると外部からアクセスできない。

(参考) VB.NET では Friend に相当する。

Internal.cs

using System;

public class Internal
{
  public static void Main()
  {
    var o = new FriendClass();
    o.Say();
  }
}

FriendClass.cs

using System;

class FriendClass
{
  internal void Say()
  {
    Console.WriteLine("Hey!");
  }
}

実行例 (mono)

$ gmcs Internal.cs FriendClass.cs -codepage:utf8
$ ./Internal.exe 
Hey!

[Top][SubIndex]


スタティックなクラスとは

スタティックなクラス (静的クラス) は Visual Basic のモジュールに相当するクラスである。スタティックなクラスはインスタンス化できないので、コンストラクタを持つことはできない。メソッドやプロパティは、すべて static 属性を持たなければならない。

using System;

public class StaticClassTest
{
  public static void Main()
  {
  }
}

// static を付けるとコンストラクタは持てない。
static class Class1
{
  public Class1()  // コンパイルエラー
  {
  }
  // メソッドはすべて static 属性が必要。
  public static void DoSomething()
  {
  }
}

[Top][SubIndex]


デストラクタ

デストラクタはクラス名に ~ を付けたもので、オブジェクトが破棄されるときにコールされる。
(注意) デストラクタはオブジェクトが消滅する直前に呼び出される。その時、他のオブジェクトが確実に存在するとは限らないので使用するときは注意すること。

using System;

public class Destructor
{
  public static void Main()
  {
    var o = new Class1();
    o = null;
  }
}

class Class1
{
  // コンストラクタ
  public Class1()
  {
    Console.WriteLine("Constructor");
  }
  
  // デストラクタ
  ~Class1()
  {
    Console.WriteLine("Destructor");
  }
}

似たものに IDisposable インターフェイスの Dispose メソッドがある。こちらはデストラクタより前にコールされる。


メソッドのパラメータ修飾子 out, ref とは

修飾子 out を付けたパラメータを使うと、メソッド内での処理結果をその変数に反映させて呼び出し側に戻すことができる。修飾子 out を付けたパラメータは「参照渡し」になる。out 同様、メソッド内の処理結果が反映される。out との違いは、パラメータとして渡す場合、必ず外部で宣言された変数であることが必要である。

using System;

public class OutRefTest
{
  public static void Main()
  {
     var o = new Class1();
     o.setId(10);
     Console.WriteLine(o.getId());
     int id;
     o.IncId(out id);  // id 出力を受け取る。
     Console.WriteLine(id);
     id = 10;
     o.DecId(ref id);  // id を参照渡しにする。
     Console.WriteLine(id);
  }
}

class Class1
{
   int _id = 0;
   
   public void setId(int id)
   {
     _id = id;
   }
   
   public int getId()
   {
      return _id;
   }
   
   // out で出力
   public void IncId(out int id)
   {
     _id++;
     id = _id;
   }
   
   // ref で参照渡し
   public void DecId(ref int id)
   {
     _id = id - 1;
     id = _id;
   }
}

[Top][SubIndex]


自動実装プロパティ

自動実装プロパティを使うとプライベート変数を明示的に書く必要がなくなる。C# 6.0 以上では値の初期化が可能になった。

using System;

public class AutoPropertyTest
{
  public static void Main()
  {
     var o = new Class1("Snoopy");
     Console.WriteLine(o.Name);
     o.Name = "Hello Kitty";
     Console.WriteLine(o.Name);
  }
}

class Class1
{
   public string Name { get; set; }  // 自動実装プロパティは get; set; と書くだけでよ い。
   
   public Class1(string name)
   {
      Name = name;
   }
}

[Top][SubIndex]


ジェネリックメソッド

ジェネリックはコレクションによく使われるがメソッドやクラスでも使用可能である。下の例はジェネリックを使って様々な型のパラメータを受け入れることができるようにしたものである。

using System;

public class GenericMethod
{
  public static void Main()
  {
     var o = new Class1(20);
     var i = o.getLarger(10);
     Console.WriteLine(i);
  }
}

class Class1
{
   IComparable _value;
   
   public Class1(object v)
   {
      _value = (IComparable)v;
   }
   
   // 現在の値 _value と与えれたパラメータ v と比べ大きいほうを返すメソッド
   // T は IComparable が実装されたオブジェクトの型 (int, long, double など)
   public object getLarger<T>(T v) where T : IComparable
   {
      if (_value.CompareTo(v) > 0)
        return _value;
      else
        return v;
   }
}

[Top] [SubIndex]


ジェネリッククラス

ジェネリックはコレクションによく使われるがメソッドやクラスでも使用可能である。

using System;

public class GenericMethod
{
  public static void Main()
  {
     var o1 = new Class1<int>(20);  // Class1 を Int32 でインスタンス化
     Console.WriteLine(o1.GetType());
     var o2 = new Class1<long>(20);  // Class1 を Int64 でインスタンス化
     Console.WriteLine(o2.GetType());
  }
}

// ジェネリッククラス
class Class1<T>
{
   T _value;
   
   public Class1(T v)
   {
      _value = v;
   }
}

[Top] [SubIndex]


拡張メソッド

拡張メソッドは既存のクラスメソッドを追加して機能を拡張するために使用する。

using System;

public class ExtendMethod
{
  public static void Main()
  {
     Console.WriteLine(DateTime.Now.ToStringDay());
  }
}

// 拡張メソッドは静的なクラス内で宣言する。クラス名は任意。
static class Class1
{
  // 拡張メソッドの第一引数は拡張対象クラスであること。また、this を付けて拡張対象であ   ることを識別する。
  public static string ToStringDay(this DateTime dt)
  {
     return String.Format("{0:MM/dd}", dt);
  }
}

[Top] [SubIndex]


インデクサ

インデクサーを使用すると、配列と同じようにオブジェクトにインデックスを付けることができる。

using System;

public class IndexerTest
{
  public static void Main()
  {
     var o = new SampleCollection<int>();
     
     for (var i = 1; i <= 100; i++)
     {
        o[i - 1] = i;
     }
     
     Console.WriteLine(o[5]);
     Console.WriteLine(o[50]);
  }
}

// インデクサ付きのクラス
class SampleCollection<T>
{
  private T[] arr = new T[100];
  
  // インデックスでオブジェクトをアクセスするためのプロパティ
  public T this[int i]
  {
     get
     {
        return arr[i];
     }
     
     set
     {
        arr[i] = value;
     }
  }
}

[Top] [SubIndex]

 

 インタフェース

インタフェースの使い方

インタフェースは interface で定義する。public などの修飾子は不要である。

using System;
using System.Linq;

public class InterfaceTest
{
  public static void Main()
  {
     int[] data = {10, 4, 22, 15, -4};
     var o = new Class1(data);
     Console.WriteLine(o.getMax());
     Console.WriteLine(o.getMin());
  }
}

// インタフェースの定義
interface IClass1
{
  int getMax();
  int getMin();
}

// インタフェースの実装
class Class1 // : IClass1
{
  int _max;
  int _min;
  
  public Class1(int[] data)
  {
     _max = data.Max();
     _min = data.Min();
  }
  
  // IClass1.getMax() の実装
  public int getMax()
  {
     return _max;
  }
  
  // IClass1.getMin() の実装
  public int getMin()
  {
     return _min;
  }
}

[Top][SubIndex]


ICloneable

System.ICloneable インタフェースを実装するとオブジェクトの複製動作をカスタマイズできる。このインタフェースは Object Clone() というメソッドのみがある。

using System;
using System.Collections;

public class ICloneableTest
{
  public static void Main()
  {
    int[] a = {1,2,3,4};
    var o1 = new Class1();
    o1.data.AddRange(a);
    Class1 o2 = (Class1)o1.Clone();  // o1 のディープコピーを取る。
    o1.data.Clear();  // o1 のデータはクリアしてしまう。
    // Class1 o2 = o1; だとデータは残らない。
    
    // o1 のディープコピーなのでデータが残っている。
    foreach (var i in o2.data)
    {
      Console.WriteLine(i);
    }
  }
}

// ICloneable が実装されたクラス
class Class1 : ICloneable
{
  public ArrayList data;
  
  public Class1()
  {
    data = new ArrayList();
  }
  
  // ICloneable.Clone() の実装
  public object Clone()
  {
    var obj = new Class1();
    obj.data.AddRange(this.data);
    return obj;
  }
}

[Top][SubIndex]


IComparable

IComparable インタフェースを実装したクラスは大小比較が可能になる。このインタフェースは int IComparable.CompareTo(object) というメソッドのみを持つ。

using System;

public class SortTest
{
  public static void Main()
  {
     Class1[] arr = new Class1[4];
     arr[0] = new Class1("123");
     arr[1] = new Class1("a");
     arr[2] = new Class1("aaaaaa");
     arr[3] = new Class1("a C");
     Array.Sort(arr);
     foreach (var x in arr)
     {
        Console.WriteLine(x.Name);
     }
  }
}

// IComparable を実装するとこのクラスを比較できるようになる。
class Class1 : IComparable
{
  public string Name { get; set; }
  public int length;
  
  public Class1(string name)
  {
     Name = name;
     length = name.Length;
  }
  
  // この場合、降順ソートになる。昇順にしたいときは、(this.length - s.length) を返せばよい。
  public int CompareTo(object obj)
  {
     Class1 s = (Class1)obj;
     return s.length - this.length;  // Name の長さで比較する。
  }
}

[Top][SubIndex]


IComparer

IComparer インタフェースは IComparable と似ているが、用途は異なり、Array.Sort(IComparer) および Array.BinarySearch(IComparer) メソッドが要素を並べ替えるときに使用する。つまり既存クラスでも、これらのメソッドに IComparer を実装したオブジェクトを渡すことで並べ替えが可能になる。

using System;
using System.Collections;

public class IComparerTest
{
  public static void Main()
  {
    string[] hex = { "1a", "b2", "1f3", "4" };
    var list = new ArrayList(hex);  // 一旦、Sort(IComparer) を持つ ArrayList にする。   
    
    // 並べ替え実行
    list.Sort(new Class1());
    foreach (var s in list)
    {
       Console.WriteLine(s);
    }
  }
}


// IComparer を実装したクラス
class Class1 : IComparer
{
  // ヘキサ文字列を値として比較する。
  public int Compare(object x, object y)
  {
     var ix = Convert.ToInt32((string)x, 16);
     var iy = Convert.ToInt32((string)y, 16);
     return ix - iy;
  }
}

[Top][SubIndex]


IEnumerable

using System;
using System.Collections;

public class IEnumerableTest
{
  public static void Main()
  {
     var o = new Class1();
     
     // IEnumerable を実装したクラスのインスタンスは foreach 可能である。
     foreach (var i in o)
     {
        Console.WriteLine(i);
     }
  }
}


// IEnumerable を実装したクラス
class Class1 : IEnumerable
{
  int[] pn = { 1, 2, 3, 5, 7, 11, 13 };
  
  // IEnumerator GetEnumerator() を実装する必要がある。
  public IEnumerator GetEnumerator()
  {
     return pn.GetEnumerator();  // pn は配列なので、その GetEnumerator() を返す。
  }
}

あるいは yield return を使って次のようにも書ける。

// IEnumerable を実装したクラス
class Class1 : IEnumerable
{
  int[] pn = { 1, 2, 3, 5, 7, 11, 13 };
  
  // IEnumerator GetEnumerator() を実装する必要がある。
  public IEnumerator GetEnumerator()
  {
     for (int i = 0; i < pn.Length; i++)
     {
       yield return pn[i];
     }
  }
}

[Top][SubIndex]


IEnumerator

IEnumerator インタフェースはコレクションの列挙のための Current プロパティ、MoveNex() メソッド、および Reset() メソッドを持つ。

using System;
using System.Collections;

public class IEnumeratorTest
{
  public static void Main()
  {
     var o = new Class1();
     
     while (o.MoveNext())
     {
        Console.WriteLine((int)o.Current);
     }
  }
}


// IEnumerator を実装したクラス
class Class1 : IEnumerator
{
  object[] data = { 1, 2, 3, 5, 7, 11, 13 };
  int p = -1;
  
  // Current プロパティの実装
  public object Current
  {
    get { return data[p];}
  }
  
  // MoveNext() メソッドの実装
  public bool MoveNext()
  {
     p++;
     return (p < data.Length);
  }
  
  // Reset() メソッドの実装
  public void Reset()
  {
     p = 0;
  }
}

[Top][SubIndex]


 

 構造体

構造体はクラスと似ているが代入したとき代入先が参照ではなく値になることに注意。

using System;

public class StructTest
{
  public static void Main()
  {
     var o1 = new Struct1() { Name = "struct1" };
     var o2 = new Class1() { Name = "class1" };
     
     // 構造体から生成したオブジェクトの場合
     var x = o1;
     Console.WriteLine(x.Name);  // x は o1 の参照なので
     o1.Name = "";               // 元のオブジェクト(o1)の値を変更すると
     Console.WriteLine(x.Name);  // x の値も変更される。
     
     // クラスから生成したオブジェクトの場合
     var y = o2;
     Console.WriteLine(y.Name);  // y は o2 の値を含めた複製なので
     o2.Name = "????";           // 元のオブジェクト(o1)の値を変更しても
     Console.WriteLine(y.Name);  // y の値は変わらない。
  }
}

struct Struct1
{
  public string Name;
}

class Class1
{
  public string Name;
}

[SubIndex][Top]

 

 名前空間


名前空間のエイリアス

長い名前空間に別名を付けることができる。

using System;
using Con = System.Console;  // System.Console のエイリアスを定義

public class NamespaceTest1
{
  public static void Main()
  {
    Con.WriteLine("System.Console");  // エイリアスを使って WriteLine メソッドをコール。   
  }
}

[SubIndex][Top]


using static

C# 6.0 以上で、using static 構文が使えるようになった。これを使うとスタティックなメソッドの呼び出しで、名前空間を省略できるようになる。

using static System.Console;  // System.Console を static としてインポート
public class Test
{
   public static void Main()
   {
      WriteLine("Hello");  // System.Console. が不要。
   }
}

[SubIndex][Top]


global::

global 名前空間は最上位の名前空間であり、暗黙的に定義される無名の名前空間である。名前空間を定義せずにクラスを定義するとそのクラスは global:: に所属する。

Windows Forms で次のようにすると Settings を簡単(短い表現で)にアクセスできる。

using MySettings = global::MyApp.Properties.Settings;

[SubIndex][Top]


 

 制御構文


as と キャスト

as と キャストはどちらもオブジェクトの型を変換するが、変換に失敗したとき as は null を返し、キャストは例外を発生させる。また、as は値型のオブジェクト(例:整数)には使用できない。

using System;

public class CastTest
{
  public static void Main()
  {
     int n = 100;
     long m = (long)n;  // OK
     m = n as long;  // n は構造体のインスタンスなのでコンパイルエラー
     
     var o1 = new Class1();
     object o = o1 as object;  // o1 はクラスのインスタンスなので OK
     Console.WriteLine(((Class1)o).Name);
     object q = (String)o;   // キャストできないので例外が発生する。
     object q = o as String;   // 変換できないので結果が null になる。
     if (q == null)
       Console.WriteLine("Null");
  }
}

class Class1
{
  public string Name = "class1";
}

[SubIndex][Top]


async / await

async /await を使うと非同期処理をわかりやすく書くことができる。

using System;
using System.Threading.Tasks;
using System.IO;

namespace AsyncIOTest
{
  /// <summary>
  /// 非同期IOメソッドのテスト
  /// </summary>
  class Program
  {
     static void Main(string[] args)
     {
        if (args.Length == 0)
        {
            Console.WriteLine("Usage: AsyncIOTest.exe filename");
            return;
        }
        
        Task task = ReadFile(args[0]);  // 非同期タスクを実行する
        task.Wait();  // 終わるまで待つ
        Console.WriteLine();
        Console.WriteLine("Done.");
#if DEBUG
        Console.ReadKey();
#endif
     }
     
     /// <summary>
     /// 非同期メソッドを使うためにasyncキーワードを使ったメソッドを別に用意する。
     /// </summary>
     /// <param name="filename"></param>
     /// <returns></returns>
     public static async Task ReadFile(string filename)
     {
        Task<string> taskStr;  // 非同期メソッドの結果を受け取るオブジェクト
        var reader = new StreamReader(filename);
        // 非同期タスクを実行する。
        await Task.Run(() =>
        {
           while (! reader.EndOfStream)
           {
              taskStr = reader.ReadLineAsync();
              taskStr.Wait();
              Console.WriteLine(taskStr.Result);  // 非同期メソッド(ReadLineAsync)が返した結果を表示する。
           }
        });
     }
}
}

[SubIndex][Top]


checked / unchecked

checked, unchecked はオーバーフローが起こった時の動作をコンパイラに指示する。

 詳細はこちらを参照

[SubIndex][Top]


dynamic

動的なオブジェクトでは実行時にメンバーを追加したりできる。動的オブジェクトを使うには DynamicObject クラスを継承し dynamic キーワードを使ってそのオブジェクトが同的オブジェクトであることをコンパイラに知らせる。

ExpandoObject クラスを使うと動的なオブジェクトを直接、作成できる。

using System;
using System.Dynamic;  // この名前空間を参照すること。

namespace CsDynamic
{
  class Program
  {
    /// <summary>
    ///  DynamicObjectクラスを継承して動的なプロパティを定義したクラス
    /// </summary>
    class TestObject : DynamicObject
    {
       // DynamicObject を継承したクラスでは存在しないメンバーをアクセスしようとすると TryGetMember または TrySetMember がコールされる。
       public override bool TryGetMember(GetMemberBinder binder, out object result)
       {
          if (binder.Name == "Product")   // Product というメンバーの値として"パイン缶"を返す。
          {
             result = "パイン缶";
             return true;
          }
          else
          {
             result = "";
             return false;
          }
       } 
    }
    
  /// <summary>
   /// 匿名型オブジェクトを返す場合、dynamicキーワードを付ける。
   /// </summary>
   /// <returns></returns>
   static dynamic getStock()
   {
      return new { Product = "イワシ缶", Count = 50 };
   }
   
   /// <summary>
   /// メインプログラム
   /// </summary>
   /// <param name="args"></param>
   static void Main(string[] args)
   {
      // DynamicObjectから派生したクラスのプロパティを使う例
      dynamic o0 = new TestObject();
      try
      {
         Console.WriteLine(o0.Product);
         Console.WriteLine(o0.Price);
      }
      catch (Exception ex)
      {
         Console.WriteLine(ex.Message);
      }
      
      // ExpandoObjectを使い、dynamic型オブジェクトに動的にプロパティを作成する。
      dynamic o1 = new ExpandoObject();
      o1.Product = "サバ缶";
      o1.Count = 100;
      Console.WriteLine(o1.Product + "," + o1.Count.ToString());
      
      // dynamicの変数に匿名型オブジェクトを代入する例
      dynamic o2 = getStock();
      Console.WriteLine(o2.Product + "," + o2.Count.ToString());
      
#if DEBUG
      Console.ReadKey();
#endif
    }
}
}

[SubIndex][Top]


event

イベントはデリゲートのコレクションでデリゲートオブジェクトを += 演算子でイベント変数に追加することでイベント待ち受けの準備を行う。イベントオブジェクトはメソッドのように呼び出し可能なので、外部からイベントオブジェクトを呼び出すことによりイベントを発生させる。

using System;

// イベントハンドラ用のデリゲートを宣言する。
public delegate void Delegate1(int n);

public class EventTest
{
  public static void Main()
  {
     // static なメソッドではイベントを使えないので staticでないオブジェクトを作る。
     var o1 = new Class1();
     // イベントを発生させる。
     o1.RaiseEvent();
  }
}

// イベントテスト用のクラス
class Class1
{
  // イベントの宣言
  public event Delegate1 OnTestEvent;
  
  public Class1()
  {
     // イベントハンドラを登録する。
     OnTestEvent += TestEvent1;
     OnTestEvent += TestEvent2;
  }
  
  // イベントを発生させるためのメソッド
  public void RaiseEvent()
  {
     OnTestEvent(1);  // TestEvent1, TestEvent2 が同時にコールされる。
  }
  
  // イベントハンドラ(1)
  public void TestEvent1(int n)
  {
     Console.WriteLine("Event1 handler = " + n.ToString());
  }
  
  // イベントハンドラ(2)
  public void TestEvent2(int n)
  {
     Console.WriteLine("Event2 handler = " + n.ToString());
  }
}

[SubIndex][Top]


extern

extern は Win32 API や独自 DLL 関数を呼び出すとき、それが非 .NET 外部関数であることを示すのに使用する。

using System;
using System.Runtime.InteropServices;

public class ExternTest
{
  [DllImport("User32.dll", CharSet=CharSet.Unicode)] 
  public static extern int MessageBox(IntPtr h, string m, string c, int type);
  
  public static void Main()
  {
     MessageBox((IntPtr)0, "User32.dll を使ってメッセージボックスを表示しました。", "Message Box", 0);
  }
}

[SubIndex][Top]


fixed

fixed は unsafe なメソッドなど確保した変数が GC (Garbage Collector) により リロケーション (ごみ拾いの都合でアドレスが変更される) されないようにする。これによりポインタの値が常に正しい位置を指すことが保証される。

fixed (int* p = &pt.x)
{
  *p = 1;
}   

[SubIndex][Top]


implicit / explicit

implicit は、あるクラスでキャストを定義したとき、暗黙の変換ができることをコンパイラに指示する。explicit は必ず明示的にキャストする必要があることを指示する。

 詳細はこちらを参照。

[SubIndex][Top]


is

is 演算子はオブジェクトと、指定した型との間に互換性があるかどうかをチェックする。

using System;

public class IsOperatorTest
{
   public static void Main()
   {
     var str = "";
     
     // 結果は True
     if (str is String)
     {
        Console.WriteLine("True");
     }
     else
     {
        Console.WriteLine("False");
     }
     
     // 結果は False
     var o = new object();
     if (o is String)
     {
        Console.WriteLine("True");
     }
     else
     {
        Console.WriteLine("False");
     }
   }
}

[SubIndex][Top]


lock

lock はオブジェクトが排他であることを示す。つまり lock されたオブジェクトを同時に複数のタスクが使用することはできない。

using System;
using System.Threading;

public class LockSample1
{
  // ロックオブジェクト
  public static ClsObject object1;
  
  // メインプログラム
  public static void Main()
  {
     // ロックオブジェクトをインスタンス化
     object1 = new ClsObject();
     
     // スレッドを作って実行させる。
     ThreadStart worker1 = new ThreadStart(DoWork1);
     ThreadStart worker2 = new ThreadStart(DoWork2);
     Thread thread1 = new Thread(worker1);
     Thread thread2 = new Thread(worker2);
     
     thread1.Start();
     thread2.Start();
     thread1.Join();
     thread2.Join();
     
     Console.WriteLine("スレッド終了");
  }
  
  // ワーカースレッド1
  public static void DoWork1()
  {
     // object1 がロックされている場合はブロックされる。
     for (int i = 0; i < 10; i++)
     {
       lock (object1)
       {
          object1.Message = "*** ワーカー1 ***";
       }
     }
  }
  
  // ワーカースレッド2
  public static void DoWork2()
  {
     for (int i = 0; i < 5; i++)
     {
       // object1 がロックされている場合はブロックされる。
       lock (object1)
       {
          object1.Message = "((( ワーカー2 )))";
       }
     }
  }
}


// ロックオブジェクト用のクラス
//   わざわざクラスを用意しなくても Object クラスでもよい。
public class ClsObject
{
  public string message;
  
  public ClsObject()
  {
    this.message = "no message";
  }
  
  // ロック解除されたときにメッセージを表示するためのプロパティ
  public string Message
  {
     get { return this.message;}
     set
     {
        this.message = value;
        Console.WriteLine(this.message);
        Thread.Sleep(500);
     }
  }
}

[SubIndex][Top]


partial

クラスや構造体に partial を付けると複数の部分が結合されてひとつのクラスや構造体になる。この機能は、Windows Forms のフォームでよく使用されている。

using System;

public class PartialTest
{
  public static void Main()
  {
     var po = new Class1() { Id = 100, Name = "Partial Object1" };
     po.Say();
  }
}

// 部分クラス 1/2
public partial class Class1
{
   public int Id { get; set; }
}


// 部分クラス 2/2
public partial class Class1
{
  public string Name { get; set; }
  
  public void Say()
  {
     Console.WriteLine(Id);  // 部分クラス 1/2 で定義されているプロパティ
     Console.WriteLine(Name);
  }
}

[SubIndex][Top]


add, remove

add, remove はプロパティの get, set アクセサに似ている。add, remove はイベントハンドラの追加、削除を行うとき、特別な処理を追加したいとき使用する。

// add, remove のあるイベントの宣言
public event Delegate1 OnTestEvent
{
   add
   {
     Console.WriteLine("AddHandler " + value.ToString());
   }
   
   remove
   {
     Console.WriteLine("RemoveHandler " + value.ToString());
   }
}

[SubIndex][Top]


stackalloc

stackalloc は unsafe なコードでスタック上にメモリを確保するのに使用する。

int* block = stackalloc int[100];

[SubIndex][Top]


typeof

typeof 演算子はパラメータとして型を取り、結果として System.Type オブジェクトを返す。

using System;

public class TypeOfTest
{
  public static void Main()
  {
     // int (System.Int32) の System.Type オブジェクトを得る。
     Type t1 = typeof(int);
     Console.WriteLine(t1);  // System.Int32
     // System.Collections.Generic.Dictionary<object, object> の System.Type オブジェクトを得る。
     Type t2 = typeof(System.Collections.Generic.Dictionary<object, object>);
     Console.WriteLine(t2);  // System.Collections.Generic.Dictionary`2[System.Object,System.Object]
     
     // ※ 変数の Type を取得するには System.Object.GetType() メソッドを使用する。
     string s = "";
     Console.WriteLine(s.GetType());  // System.String
  }
}

[SubIndex][Top]


volatile

volatile キーワードは、同時に実行中の複数のスレッドによってフィールドが変更される可能性があることを示す。これは、コンパイラが最適化の対象にしないことを意味する。最適化では場合によっては、変数はレジスタに割り当てられるため volatile がない場合、問題が発生する可能性がある。

public volatile int i;

[SubIndex][Top]


yield

yield は return (あるいは break) とともに使用する。通常、IEnumerable インタフェースを実装したクラスの IEnumerator GetEnumerator() メソッド内で列挙可能な値を返すのに使用する。

// IEnumerable を実装したクラス
class Class1 : IEnumerable
{
  int[] pn = { 1, 2, 3, 5, 7, 11, 13 };
  
  // IEnumerator GetEnumerator() を実装する必要がある。
  public IEnumerator GetEnumerator()
  {
     for (int i = 0; i < pn.Length; i++)
     {
       yield return pn[i];
     }
  }
}

[SubIndex][Top]


 

 属性

属性 (Javaでのアノテーション) はクラスなどの宣言に対するメタ情報で、コンパイラが属性を見て付加的なコードを生成する。

共通の属性

Serializable 属性

Serializable 属性を付加すると永続化可能なクラスとマークされる。これは ISerializable インタフェースを実装したのと同じ効果を持つ。

using System;
using System.IO;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;

public class SerializableTest
{
  public const string FileName = @"C:\temp\SerializableTest.bin";
  
  public static void Main()
  {
     var o = new Class1() { Name = "", ID = 0 };
     if (File.Exists(FileName))
     {
        // 前の状態を回復する。
        Load(FileName, ref o);
     }
     
     // 状態を変更
     o.Name = "class1";
     o.ID = o.ID + 100;
     Console.WriteLine(o);
     
     // 状態を保存する。
     Save(FileName, o);
  }
  
  // 前の状態を回復する。
  public static void Load(string filename, ref T obj)
  {
     var streamIn = File.OpenRead(filename);
     var deserializer = new BinaryFormatter();
     obj = (T)deserializer.Deserialize(streamIn);
     streamIn.Close();
  }
  
  // 状態を保存する。
  public static void Save(string filename, T obj)
  {
     var streamOut = File.Create(filename);
     var serializer = new BinaryFormatter();
     serializer.Serialize(streamOut, obj);
     streamOut.Close();
  }
}


// Serializable 属性を付けるとシリアライズ可能になる。
// これは、ISerializable インタフェースを実装したのと同じである。
[Serializable]
class Class1
{
  public string Name { get; set; }
  public int ID { get; set; }
  
  public override string ToString()
  {
     return Name + " " + ID.ToString();
  }
}

[Top] [SubIndex]


DllImport 属性

DLLImport 属性はマネージコード (つまりC#) からネイティブコード (Win32) を呼び出すときに使用する。

using System;
using System.Runtime.InteropServices;

public class ExternTest
{
  [DllImport("User32.dll", CharSet=CharSet.Unicode)] 
  public static extern int MessageBox(IntPtr h, string m, string c, int type);
  
  public static void Main()
  {
     MessageBox((IntPtr)0, "User32.dll を使ってメッセージボックスを表示しました。", "Message Box", 0);
  }
}

MSDN DllImport 属性の使用

[Top] [SubIndex]


In および Out 属性

参照渡しのパラメータに対して In と Out を明示するのに使用できる。

using System;
using System.Runtime.InteropServices;

public class InOutAttrTest
{
  public static void Main()
  {
     byte x = 0xef;
     refTest(ref x);
     Console.WriteLine("{0:x}", x);
  }
  
  public static void refTest([In][Out] ref byte b)
  {
     b ^= 0x55;
  }
}

[Top] [SubIndex]


共用体

C / C++ には「共用体」(union) というのがありますが、C# にはありません。ですが、属性 StructLayout(LayoutKind.Explicit) と FieldOffset を使って実現は可能です。

using System;
using System.Runtime.InteropServices;

// C# 共用体のテスト
public class UnionTest
{
  public static void Main()
  {
     var bytes = new TBytes();
     bytes.Word = 0x12345678;
     Console.WriteLine("{0:x}", bytes.Byte0);  // 78
     Console.WriteLine("{0:x}", bytes.Byte1);  // 56
     Console.WriteLine("{0:x}", bytes.Byte2);  // 34
     Console.WriteLine("{0:x}", bytes.Byte3);  // 12
  }
}

// 詳しい解説
// https://msdn.microsoft.com/ja-jp/library/acxa5b99.aspx
[StructLayout(LayoutKind.Explicit)]
public struct TBytes
{
  [FieldOffset(0)]
  public byte Byte0;
  
  [FieldOffset(1)]
  public byte Byte1;
  
  [FieldOffset(2)]
  public byte Byte2;
  
  [FieldOffset(3)]
  public byte Byte3;
  
  [FieldOffset(0)]
  public uint Word;
}

[Top] [SubIndex]

 

 コメント

コメントは C++ や Java と同じであるが、/// の後のコメントは特別な意味を持つ。これらのコメントは XML 化されてヘルプドキュメントとして利用できる。

コメント内で改行するときは、<br> を使用する。

MSDN: ドキュメント コメント用の推奨タグ

/// <summary>DoWork is a method in the TestClass class. </summery>

[Top]

 

 Linq

Linq は SQL の文法に似たクエリ統合言語である。

MSDN: LINQ クエリ式

Linq 関連キーワード


from, where, select を使ったサンプル

using System;
using System.Collections.Generic;
using System.Linq;

public class LinqTest1
{
  public class Train
  {
     public string Name { get; set; }
     public string Line { get; set; }
     public int Kind { get; set; }
  }
  
  public static void Main()
  {
     var trains = new List() { 
       new Train(){ Name="こだま", Line="東海道・山陽", Kind=0 },
       new Train(){ Name="ひかり", Line="東海道・山陽", Kind=1 },
       new Train(){ Name="のぞみ", Line="東海道・山陽", Kind=2 },
       new Train(){ Name="さくら", Line="山陽・九州", Kind=1 },
       new Train(){ Name="みずほ", Line="山陽・九州", Kind=2 },
       new Train(){ Name="つばめ", Line="九州", Kind=0 },
       new Train(){ Name="やまびこ", Line="東北", Kind=1 },
       new Train(){ Name="なすの", Line="東北", Kind=0 }
     };
     
     var res = from x in trains where x.Kind == 0 select new {Name=x.Name, Line=x.Line};   
     foreach (var x in res)
     {
        Console.WriteLine(x.Name + " " + x.Line);
     }
  }
}

[Top] [SubIndex]


GROUP BY

using System;
using System.Collections.Generic;
using System.Linq;

public class LinqTest2
{
  // Train クラス
  public class Train
  {
     public string Name { get; set; }
     public string Line { get; set; }
     public int Kind { get; set; }
  }
  
  // メインプログラム
  public static void Main()
  {
     var trains = new List() { 
       new Train(){ Name="こだま", Line="東海道・山陽", Kind=0 },
       new Train(){ Name="ひかり", Line="東海道・山陽", Kind=1 },
       new Train(){ Name="のぞみ", Line="東海道・山陽", Kind=2 },
       new Train(){ Name="さくら", Line="山陽・九州", Kind=1 },
       new Train(){ Name="みずほ", Line="山陽・九州", Kind=2 },
       new Train(){ Name="つばめ", Line="九州", Kind=0 },
       new Train(){ Name="はやぶさ", Line="東北", Kind=2 },
       new Train(){ Name="やまびこ", Line="東北", Kind=1 },
       new Train(){ Name="なすの", Line="東北", Kind=0 },
       new Train(){ Name="あさま", Line="北陸", Kind=0 },
       new Train(){ Name="とき", Line="上越", Kind=0 }
     };
     
     // GROUP BY の例
     // res は System.Linq.Lookup`2+Grouping[System.String,  System.String] 型
     var res = from x in trains group x.Line by x.Line;
     foreach (var x in res)
     {
        Console.WriteLine(x.Key);  // Key プロパティを表示する。
     }
  }
}

[Top] [SubIndex]


ORDER BY

using System;
using System.Collections.Generic;
using System.Linq;

public class LinqTest3
{
  // Train クラス
  public class Train
  {
     public string Name { get; set; }
     public string Line { get; set; }
     public int Kind { get; set; }
  }
  
  // メインプログラム
  public static void Main()
  {
     var trains = new List() { 
       new Train(){ Name="こだま", Line="東海道・山陽", Kind=0 },
       new Train(){ Name="ひかり", Line="東海道・山陽", Kind=1 },
       new Train(){ Name="のぞみ", Line="東海道・山陽", Kind=2 },
       new Train(){ Name="さくら", Line="山陽・九州", Kind=1 },
       new Train(){ Name="みずほ", Line="山陽・九州", Kind=2 },
       new Train(){ Name="つばめ", Line="九州", Kind=0 },
       new Train(){ Name="はやぶさ", Line="東北", Kind=2 },
       new Train(){ Name="やまびこ", Line="東北", Kind=1 },
       new Train(){ Name="なすの", Line="東北", Kind=0 },
       new Train(){ Name="あさま", Line="北陸", Kind=1 },
       new Train(){ Name="とき", Line="上越", Kind=1 }
     };
     
     // ORDER BY の例
     var res = from x in trains orderby x.Kind descending select new { Name=x.Name, Line=x.Line, Kind=x.Kind};
     foreach (var x in res)
     {
        Console.WriteLine(x.Kind.ToString() + " " + x.Name + " " + x.Line);
     }
  }
}

[Top] [SubIndex]


JOIN

using System;
using System.Collections.Generic;
using System.Linq;

public class LinqTest4
{
  // Train クラス
  public class Train
  {
     public string Name { get; set; }
     public string Line { get; set; }
     public int Kind { get; set; }
  }
  
  public class Fast
  {
     public string FastType { get; set; }
     public int Kind { get; set; }
  }
  
  // メインプログラム
  public static void Main()
  {
     var trains = new List()
     {
       new Train(){ Name="こだま", Line="東海道・山陽", Kind=0 },
       new Train(){ Name="ひかり", Line="東海道・山陽", Kind=1 },
       new Train(){ Name="のぞみ", Line="東海道・山陽", Kind=2 },
       new Train(){ Name="さくら", Line="山陽・九州", Kind=1 },
       new Train(){ Name="みずほ", Line="山陽・九州", Kind=2 },
       new Train(){ Name="つばめ", Line="九州", Kind=0 },
       new Train(){ Name="はやぶさ", Line="東北", Kind=2 },
       new Train(){ Name="やまびこ", Line="東北", Kind=1 },
       new Train(){ Name="なすの", Line="東北", Kind=0 },
       new Train(){ Name="あさま", Line="北陸", Kind=1 },
       new Train(){ Name="とき", Line="上越", Kind=1 }
     };
     
     var kinds = new List()
     {
       new Fast(){ FastType="速達型", Kind=2 },
       new Fast(){ FastType="中間型", Kind=1 },
       new Fast(){ FastType="各駅停車", Kind=0 }
     };
     
     // JOIN の例
     var res = from x in trains join y in kinds on x.Kind equals y.Kind orderby x.Kind descending select new { Name=x.Name, Line=x.Line, Kind=y.FastType};
     foreach (var x in res)
     {
        Console.WriteLine(x.Kind + " " + x.Name + " " + x.Line);
     }
  }
}

[Top] [SubIndex]


let の使用例

let を使うと、クエリー式の中で演算を行いその結果を利用できる。

using System;
using System.Collections.Generic;
using System.Linq;

public class LinqTest5
{
  // メインプログラム
  public static void Main()
  {
     var cur = new List() { "USD (United States)", "EUR (Europe Union)", "JPY (Japan)", "GBP (United Kingdom)" };
     var rs = from x in cur let m = x.Substring(0, 3) select m;  // 通貨略称のみを取り出す。
     foreach (var x in rs)
     {
        Console.WriteLine(x);
     }
  }
}

[Top] [SubIndex]

 

 Lambda 式

using System;
using System.Collections.Generic;

public class LambdaTest
{
  public static void Main()
  {
     var strlist = new List() { "cat", "dog", "mouse", "chicken" };
     
     // ForEach は Action デリゲートを引数に持つ。ここでは Action をラムダ式で書いている。
     strlist.ForEach((s) => Console.WriteLine(s.ToUpper()));
     
     // 上の文は delegate を用いて書くこともできる。
     strlist.ForEach(delegate(string s) { Console.WriteLine(s.ToUpper()); });
     
     // ラムダ式は代入もできる。
     Action action = (s) => Console.WriteLine(s.ToUpper());
     strlist.ForEach(action);
  }
}

[Top]

 

 Delegate

delegate の使い方

delegate は匿名メソッドを作るときに使用できる。使用例はラムダ式を参照。ただし、ラムダ式でも同じことが可能なので普通、ラムダ式を使う。C# の古いバージョンではラムダ式が使えないので、その場合は delegate を使用するしかない。

delegate はメソッドの「型」を宣言する場合にも使用する。コールバックを行う場合などによく使用される。

using System;

// delegate を宣言 (メソッドの型)
public delegate bool DoIt(string verb);

public class DelegateTest
{
  public static void Main()
  {
     DoIt doit;  // delegate 型の変数を宣言
     var o = new Class1();
     doit = o.Say;  // delegate 型の変数に Class1 の Say メソッドを代入
     doit("Hey");   // メソッドを呼出し。
  }
}

class Class1
{
  public bool Say(string str)
  {
     if (str.Length == 0)
       return false;
     Console.WriteLine(str);
     return true;
  }
}

汎用的に使用できるデリゲート

よく使われるデリゲートはわざわざ宣言しなくてもシステムで用意されている。

[@IT] 汎用的に使用できるデリゲート

bool Predicate(T obj) デリゲートの使用例を示す。

using System;

public class PredicateTest
{
  public static void Main()
  {
     // Predicate デリゲートの宣言
#if LAMBDA
     // ラムダ式を使う場合
     Predicate pred = (n) => n < 0;
#else
     // ラムダ式を使わない場合
     Predicate pred = delegate(int n) { return n < 0; };
#endif
     int[] arr = { 10, -1, 3, 9, 21, -11, -6 };
     // Predicate の条件に合う最初の要素を返す。
     int first = Array.Find(arr, pred);
     Console.WriteLine(first);
}
}

[Top]

 

 

このページのトップへ