トップページ
ひらく | たたむ | ページトップ
↓マウスで反転選択した文字を検索
C#
   
ページ内検索 ページ外検索
検索したい文字を入力して
ENTERを押すと移動します。
\n
[ トップページ ] [ Frameなし版 ] [ リンク集 ]  
[ ____CommandPrompt ] [ ____JScript ] [ ____MySQL ] [ ____Cygwin ] [ ____Java ] [ ____Emacs ] [ ____Make ] [ ____Perl ] [ ____Python ] [ ____OpenGL ] [ ____C# ] [ ____StyleSheet ] [ ____C++ ] [ ____Winsock ] [ ____Thread ] [ ____VisualStudio ] [ ____C ] [ ____Win32API ] [ ____Lua ] [ ____PhotoShop ]
ヘッダ検索
___

■ 文字列(String)




___

■ 置換

    // 置換
    path = "d:\\tmp\\test.txt";
    path.Replace( "\\", "/" );
___

■ 長さ

    s.Length;
___

■ 分割

    char[] d = new char [] {','};

    string [] a = s.Split(d);

    foreach (string st in a ) {
        Console.WriteLine(st);
    }
___

■ 結合

SYNTAX String.Join( 区切り文字列, 文字列配列 )
    // static method による join

    string [] a = { "a", "bc", "de" };
    string ret = String.Join( "-", a );

___

■ 検索

  // 検索
  WARNING 
    引数に空文字列("")を指定すると0を返す

  if ( s.IndexOf( "foo" ) >= 0 ){

  }

  // 1 番目以降
  s.Substring( 1 );

// 部分文字列 s.Substring( 1, 4 );
    string s = "aaa";
___

■ 変換

    string s = "d:/test.txt";

    // すべて小文字, 大文字
    s.ToLower();
    s.ToUpper();
___

■ cast

文字列から数値型へ
    int num1    = int.Parse("123");
    long num2   = long.Parse("123");
    float num3  = float.Parse("123.4");
    double num4 = double.Parse("123.4");
___

■ cast


  POINT
    C# は強い型チェックをする。
    そのため、型を変更するには明示的なキャストをする必要がある。
    

    文字列から数値型へ
    int num1    = int.Parse("123");
    long num2   = long.Parse("123");
    float num3  = float.Parse("123.4");
    double num4 = double.Parse("123.4");
数値型どうしでは 型名をつける。
    double d = 0.1;
    float f = ( float )d;
    

    float f = ( float )d;
    
より精度の高い型へは暗黙的な変換がゆるされる。
    float f = 0.5f;
    double d = f;

    short s = 2;
    int i = s;
WARNING 精度が低い型へはコンパイル時にエラーになる。
    int i = 128;
    short s = i;
BASIC のようなインタプリターでは実行時に桁あふれチェックをするが 実行効率を重視する C# コンパイラでは コンパイル時にチェックをする。 扱う数字が小さければ問題ないが、事前にバグを防ぐためエラーとする。 もし扱う数値が小さいと承知しているならば, キャストをしてコンパイラに通知する。
    short s = (short)i;
___

■ コレクション(Collection)


___

■ hash

連想配列

   Hashtable tbl = new Hashtable ();
   tbl ["key"] = "value";


   // 初期化子リストで生成
   Hashtable tbl = new Hashtable(){ {1, "foo"}, {2, "bar"} };

   for( DictionaryEntry i in tbl ) {
      Console.WriteLine( i.Key );
      Console.WriteLine( i.Value );
   } 


   tbl.Clear();
___

■ list

List< Foo > a = new List< Foo> ();
___

■ ArrayList

POINT 配列のサイズが動的にかわる場合は ArrayList を使う。

    using System.Collections;

    // リストをつくる。
    ArrayList a = new ArrayList();

    // リストに要素を追加する。
    a.Add( 10 );

    // 異なる型のオブジェクトも追加できる。
    a.Add( "test" );

    // 要素数を取得する
    a.Count;
    
    // 各要素にアクセスする。 generic 型のコンテナのため cast が必要。
    int i = a[0];

    // すべての要素を削除してリストを空にする。
    a.Clear();

    // index を指定して削除する
    a.RemoveAt( 0 );

    // 要素を指定して削除する。複数候補があるときは最初にマッチしたものが削除される
    a.Remove( "test" );

___

■ ソート順を操る

POINT Array は動的に 配列サイズを増やすことができない. IComparable は Sort 基準を指定する Interface  .NET Frameworkには、一次元リスト構造でデータを管理する一種の配列である ArrayList クラス(System.Collections.ArrayList)がある。 配列に似ているが、配列では実現できない機能を持っている。 動的にサイズを増加させるというのは、配列ではできないが、ArrayList ならできる。 ArrayList には、Sort という便利なメソッドがある。 問題になるのは、ソートの並び順をどうやって指定するかである。 込み入った自作クラスのインスタンスを詰め込んだArrayListをソートする場合、 どんな基準で並べ替えるべきなのだろうか。 インターフェイス IComparable を実装する、というものだ。 ArrayList に格納するインスタンスが、すべて、このインターフェイスを実装していれば、 これを基準にしてSortメソッドは並べ替えをできる。 IComparable を実装することによって プログラマはソートの並び順を思いどおりにコントロールできる。 using System; using System.Collections; namespace ConsoleApplication100 { // class ClassSample : IComparable { public string number; // 比較するための 基準を実装 public int CompareTo( object obj ) { // Cast OK よ ^ ^/ ( 安全でないけどね. ^ ^/ ) string s = ((ClassSample)obj).number; if( number == "one" && s == "two" ) return -1; if( number == "one" && s == "three" ) return -1; if( number == "two" && s == "one" ) return 1; if( number == "two" && s == "three" ) return -1; if( number == "three" && s == "one" ) return 1; if( number == "three" && s == "two" ) return 1; return 0; } public ClassSample( string s ) { number = s; } } class Class1 { static void Main(string[] args) { ArrayList al = new ArrayList(); // ArrayList.Add() がある. al.Add( new ClassSample("two") ); al.Add( new ClassSample("three") ); al.Add( new ClassSample("one") ); // sort する al.Sort(); foreach( ClassSample cs in al ) { Console.WriteLine( cs.number ); } } } } クラスにIComparableインターフェイスを実装し、CompareToメソッドを記述する。 文字列が英単語の意味順にソートされる。  IComparableは、CompareTo というメソッドを1つだけ持つ。 このメソッドは、自分自身と、引数として渡されたオブジェクトobjを比較して、 小さければ負数、 同じなら0、 大きければ正数を返す。 ここでは文字列one、two、threeを数値の1、2、3に見立てた結果を返している。 もちろん SortメソッドからCompareToメソッドが何度も呼び出されて、 その情報を基準にして並べ替えが行われているのである。 外部から条件を指定してソート  上の例は、ArrayListに格納されるインスタンス自身が、 例えば 住所録なら、名前順でソートしたいときや、住所順でソートしたいときがある。 そういう場合は、ソート順をクラスの外部で指定するという方法もある。 この方法なら、さまざまなソート方法を準備しておき、それを切り替えながら利用できる。
 using System.Collections;
 {

        // 比較するためのクラスを利用する。
   class ClassComparer : IComparer
   {
          // 比較の実装
     public int Compare( object x, object y )
     {
       double dx = (double)x;
       double dy = (double)y;
       return (int)dx - (int)dy;
     }
   }

   class Class1
   {
     static void Main(string[] args)
     {
       ArrayList al = new ArrayList();
       al.Add( 2.2 );
       al.Add( 3.7 );
       al.Add( 1.5 );
       al.Add( 2.7 );
       al.Add( 3.2 );
       al.Add( 1.1 );

       // すべての要素を削除       
       al.Clear();

       // 要素数
       int nr = al.Count;

       al.Sort( new ClassComparer() );
       foreach( double d in al )
       {
         Console.WriteLine( d );
       }
     }
   }
 }
2つの値を比較するCompareメソッドだけを記述したクラスを作成すればよい。 このプログラムでは各数値の整数部だけでソートしている。 つまり実数をソートするのだが 順番は整数扱いで決めるというものだ。 小数部は昇順に揃っていないが、整数だけは揃っているという変な状態を作り出している。 さてこれを実現するには、 Sortメソッドの引数にIComparerを渡す。 26行目にその実例が記述されている。 ここではClassComparerクラスのインスタンスが渡されているが、 これにはIComparerが実装されているので、 自動的にそれが取り出されて渡される。  さてIComparerは実装しなければ使えないので、 IComparerを実装するクラスとして、 ClassComparerクラスを定義している。 IComparerは1つのメソッドCompareを含んでいるので、これを実装する。 8行目の引数のx、yは比較すべき2つのオブジェクトである。 これらの値は実数になるはずなので、一度doubleにキャストしてやる。 そして12行目で整数にキャストして比較する。 この比較により、小さければ負数、同じなら0、大きければ正数を返すという条件が満たされる。 Sortメソッド実行時には、何度もCompareメソッドが呼び出され、 その結果により並べ替えが行われる。
___

■ Dictionary

DESC キーと値のコレクションを表します。 連想配列のこと。 キー、値ともに、文字列、数値、オブジェクトを利用できる。
        {
            Dictionary< string,int> bbdic = new Dictionary<  string,int >();

            // 追加する。
            bbdic.Add( "Rooney", 8 );
            bbdic["Ronaldo"] = 7;
            
            // 既にあるキーはエラーになる。
            try
            {
                bbdic.Add("Rooney", 18);
            }
            catch
            {
                Console.WriteLine("Already added . Rooney = {0}", bbdic["Rooney"]);
            }
            // キーが無い場合もエラー。
            try
            {
                Console.WriteLine("Saha = {0}", bbdic["Saha"]);
            }
            catch
            {
                Console.WriteLine("Not exists Key = Saha ...");
            }

            Console.WriteLine("Dictionaryの内容を表示");
            foreach (KeyValuePair< string, int> bx in bbdic)
            {
                Console.WriteLine("key = {0}, value = {1}", bx.Key, bx.Value );
            }

        }
___

■ IEnumerable インターフェース

POINT IEnumerable インターフェースを利用することで Collection の種類に関係なく同じようにデータを順にとりだすことができる。 逆に Collection というクラスが, 同一の Interface を実装してくれるおかげで 利用する側は同じコードを書くことができる。
    int[] a = new int[]{1, 3, 5};

    for(int i=0; i< a.Length; ++i) {
      Console.Write("{0}\n", a[i]);
    }
   // List を使った実装. 
   List list = new List();
   list.Add(7);
   list.Add(5);
   list.Add(3);
   list.Add(1);
   for(Node n=list.head; n!=null; n=n.next)
   {
     Console.Write("{0}\n", n.elem);
   }
コレクションクラスは共通のインターフェースを実装するという決まりを作り、 このインターフェースから要素へのアクセスすることが一般的。 そのためのクラスとして .NET Framework には IEnumerable というインターフェースが用意されている。 C# の配列は IEnumerable インターフェースを実装している。 POINT 実は次のように展開される。
   IEnumerator e = array.GetEnumerator();
   while(e.MoveNext())
   {
     型名 変数 = (型名)e.Current;
     文
   }
   // Enumerator 経由で Access する
   int[] array = new int[]{1, 3, 5, 7};

   IEnumerator e = array.GetEnumerator();
   while(e.MoveNext())
   {
     int val = (int)e.Current;
     Console.Write("{0}\n", val);
   }

   // foreach 文を利用する. 
   foreach(型名 変数 in コレクション)
     文


   int[] array = new int[10]{1, 2, 4, 8, 16, 32, 64, 128, 256, 512};

   foreach(int n in array)
   {
     Console.Write(n + " ");
   }
___

■ Enumerator.を自作する




___

■ XML


___

■ XMLDocument


  using System.Xml;
            {
                //インスタンスの生成
                XmlDocument doc = new XmlDocument();

                //XMLの読み込み
                doc.Load("d:/test.xml");

                //ルートノードの参照
                XmlNode root = doc.DocumentElement;
                
                //要素ノードの作成して追加
                XmlElement e = doc.CreateElement("test");
                root.PrependChild( e );
                
                //テキストノードの作成
                XmlCharacterData text = doc.CreateTextNode("foo");
                e.PrependChild( text );
                
                // Attribute 追加
                e.SetAttribute( "id", "10" );

                //XMLの保存
                doc.Save( "d:/out.xml" );
            }
___

■ ファイル(File)


___

■ 書き込み


  using System.IO;

        {
            FileStream f = new FileStream( "d:/test.txt", FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite );

            StreamWriter s = new StreamWriter( f );
            s.WriteLine("aaa");
            s.WriteLine("bbb");
            s.Flush();
        }

___

■ ロック(Lock)


        {
            FileStream f = new FileStream( "d:/test.txt", 
                      FileMode.Create, 
                      FileAccess.ReadWrite, 
                      FileShare.ReadWrite
                      );

            StreamWriter s = new StreamWriter( f );
            s.WriteLine("aaa");
            s.WriteLine("bbb");
            s.Flush();

            // ファイル全体をロックする
            f.Lock(0, f.Length);

            // 何か長い処理をする。
            // ロック中は他のプロセスはアクセスできない。
            // プロセスはファイルにアクセスできません。別のプロセスが使用中です。
            Thread.Sleep( 30 * 1000 );

            f.Unlock(0, f.Length);
        }
        
        
            // ロックをすることで、他のプロセス(コード)からアクセスすると IOException が発生する。
            f.Lock(0, f.Length);

            FileStream f2 = new FileStream( "d:/test.txt", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite );

            StreamWriter s2 = new StreamWriter( f2 );
            s2.WriteLine("aaa");
            s2.WriteLine("ccc");

            // 厳密には Write() で追加したバッファの内容がファイルへ転送した瞬間におきる。
            s2.Flush();
            s2.Close();

___

■ OS



{
        {
            OperatingSystem os = Environment.OSVersion;
            Console.WriteLine( os.Platform + " " + os.Version.Major );
        }
}

___

■ 配列(Array)


  POINT
    C# の配列はひとつのクラスのため Instance を生成する時は new をする。
    System.Array クラスから継承される。
    次元は N 次元可能で, 宣言. 初期化の構文以外は C, C++ と同じ.
    配列は、[ 同じデータ型の多数のデータを集めたもの ]で、個々のデータはインデックス番号で読み書きできる。

  REFERENCE   ArrayList 
    要素数が 可変の場合は ArrayList をつかう。


  WARNING 
    [] は読み書きに使うときには変数名の後に付くが、宣言時には変数名の手前になる。
    int []a = new int[10]; 
   // int 型要素が 10 個はいる配列インスタンスを a という名前で作成
   int[] a = new int[10];

    // data を代入して初期化
    // { 1, 2... } 初期化子リスト 
   int[] a = new int[10]{1, 2, 4, 8, 16, 32, 64, 128, 256, 512};

   int [] a = { 4, 1, 3, 2, 0 };

   // 昇順に Sort
   System.Array.Sort( a );


   // 要素のインデックスを検索する( なければ -1 ) 
   Array.IndexOf( a, 1 );


   // 2次元配列
   int[,] a = new int[3,2];
   int[,] a = new int[3,2]{ {1,1}, {1,1}, {1,1} };

   // 各要素へは , 区切りで指定する
   a[0,0] = 1;

   // 3次元以上も同じ
   int [,,] ar = new int[3,3,3];
   Console.Write( ar[0,0,0] );
   


   // 各要素をイテレートする
   // IEnumerableというインターフェイスの実装を含む。
   foreach( int i in a ) {
     
   }
      
    // 参照型の配列の初期化
    Class1 [] ar = new Class1 [] { new Class1(1), new Class1(2), new Class1(3) };
___

■ 参照型の配列

POINT 参照型の配列の場合は, 配列自体の Instance の作成だけでなく, 要素を new した インスタンスをセットする必要がある。 各要素は参照型のため値は null となっている。 利用するには各要素ごとにさらにインスタンスをつくり、その値をセットしておく必要がある。 要素の数が10個なら、配列自身と要素を合わせて、11回のnewをする。
     {
          // インスタンスを作成。 
       string [] ar = new string[10];
 
       for( int i=0; i< 10; i++ )
       {
         // 実行時に null を参照したというエラーになる
         //Console.WriteLine( ar[i].Length );  
       }

       for( int i=0; i< 10; i++ )
       {
         ar[i] = (i*2).ToString();
       }

       for( int i=0; i< 10; i++ )
       {
         Console.WriteLine( ar[i].Length );
       }
     }

___

■ 初期化

 配列を宣言する際に、決まりきった値をあらかじめ決め打ちで入れてしまいたい場合がある。 また配列を作成時に 初期化を設定できる。
     {
          // 要素初期化つきで宣言. 
       int [] ar1 = new int [4] { 0, 1, 2, 3 };
       int [] ar2 = new int [] { 0, 1, 2, 3 };

          // 省略系もある。 
       int [] ar3 = { 0, 1, 2, 3 };

       for( int i=0; i< 4; i++ )
       {
         Console.WriteLine( "{0},{1},{2}", ar1[i], ar2[i], ar3[i] );
       }
     }
___

■ ソート

POINT static System.Array.Sort() を利用してソートできる。 ソートするときの比較条件を指定する場合は, IComparable.CompareTo() を実装する。 IComparable インターフェイスを通じて、CompareTo メソッドが呼ばれる。  CompareTo メソッドは、インスタンス自身と引数のインスタンスを比較して その結果として、整数型の値を返す。 返値は、引数インスタンスよりも小さいときは、負の値、同じなら0、大きければ正の値を返すのがルール。
 {
      // Sort の条件は IComparable を実装することで実現できる.
   class Item : IComparable
   {
     public string name;
     public int price;

     public Item( string name, int price )
     {
       this.name = name;
       this.price = price;
     }

        // 比較する必要な Interface
        // ここでは値段で比較.
     public int CompareTo( object obj )
     {
       return price - ((Item)obj).price;
     }
   }

   class Test
   {
     static void Main(string[] args)
     {
       Item [] ar = {
         new Item("チョコ",50),
         new Item("煎餅",200),
         new Item("飴",10),
         new Item("ポテチ",100)
       };

       // Itemクラスのインスタンスをソートする
       Array.Sort( ar );

       foreach( Item it in ar )
       {
         Console.WriteLine("{0},{1}",it.name,it.price);
       }
     }
   }

___

■ バイナリ・サーチ

POINT System.Array クラスには ソートだけでなくバイナリ・サーチも利用できる。
     {
       int [] ar = { 4,9,3,6,1,0 };
       Array.Sort( ar );
       foreach( int n in ar )
       {
         Console.WriteLine( n );
       }

          // Array.BinarySearch() で検索可能
       int index = Array.BinarySearch( ar, 3 );
       Console.WriteLine( "index of 3 is {0}", index );
     }
値が“3”である要素のインデックスが“2”であることが分かる。 POINT Array.BinarySearch() を利用することで, BinarySearch を利用できる。 ただしソートすみである必要がある. POINT  配列型はれっきとしたデータ型なので、メソッドの引数に使ったり、 メソッドの戻り値として使ったりできる。 配列型は要素数を限定しないため、大きさの決まっていない配列を受け渡すことができる。
 {
   class Class1
   {
     static public int [] makeArray( bool select )
     {
       if( select )
       {
         int [] ar = { 0,1,2,3 };
         return ar;
       }
       else
       {
         int [] ar = { 0,1 };
            // 配列型をかえす.
         return ar;
       }
     }

        // 配列型をうけとる.
        // 要素が何個の配列でも受け取ることができる。
     static public void dumpArray( int [] ar )
     {
       foreach( int n in ar )
       {
         Console.WriteLine( n );
       }
     }

     static void Main(string[] args)
     {
       int [] ar = makeArray(false);
       dumpArray( ar );

       Console.WriteLine();

          // 配列変数( 参照 )を再度代入することも可能. 
          // 29 の 配列 Instance は参照がなくなるので, 消えることになる. 
       ar = makeArray(true);

       dumpArray( ar );
     }
   }
 }
要素数に関係なく、配列を受け渡しできる。 POINT 配列変数への代入は, 配列 Instance を再利用しないで, 新規に作成される. 各要素の表示にforeach文を使用すれば、要素の個数を調べる必要はない。 int []が指定されていることから分かるとおり、 このメソッドは、整数の配列を返す。 要素数に関係なく、戻値として渡すことができる。 WARNING 配列変数への代入は、配列の再利用にはならず、 別個の配列インスタンスが生成される。
___

■ 参照型配列の初期化

POINT 参照型の配列も初期化できる
 {
   class Class1
   {
     private int n;
     public Class1( int param )
     {
       this.n = param;
     }
     static void Main(string[] args)
     {
       for( int i=0; i< 3; i++ )
       {
         Console.WriteLine( ar[i].n );
       }
     }
   }
 }
配列の要素として Class1 のインスタンスを設定している。 配列作成時にClass1のコンストラクタに渡した値が順に表示される。 POINT 初期化リストの中に Class1クラスのインスタンスを作成するnewを書き込んでいること。 配列のメソッド POINT 配列型は クラスであり, System.Array から継承されたもの Array の Method はすべて使用できる。 配列変数は、一種のインスタンスである。 システムに標準で含まれる System.Array クラスから継承されたものであり、 このクラスが公開するメソッドなどはすべて利用できる。
 {
     {
          // 
       int [] ar = { 0, 1, 2, 3, 4 };

          // Array.Method を利用可能である. 
       for( int i=ar.GetLowerBound(0); i< =ar.GetUpperBound(0); i++ )
       {
         Console.WriteLine( ar[i] );
       }
     }

配列の下限から上限までの各要素の値が順に表示される。 GetLowerBoundメソッドは、配列の添え字の下限を返す。 GetUpperBoundメソッドは、添え字の上限の値を返す。 それぞれ、引数の0というのは、最初の次元を意味する。 1次元配列の場合は常にゼロだが、2次元以上になると、何次元目の上限、下限の値を得るか示すために、 0から次元数マイナス1の値のいずれかを指定する。 配列はただ配列なのではなく それ自身がインスタンスとしての側面も持つことを忘れてはならない。
___

■ foreach

 foreach 文の効能により ループをまわりながら 配列の要素が1つずつ変数nに代入される。 これにより 配列の添字の範囲を意識することなく、すべての要素にアクセスできる。 WARNING   foreach文でアクセスした場合、配列の内容を書き換えられないこと。 この場合、変数nの値を書き換えても、変数nが変わるだけであって 配列の要素の値が変わることにはならない。
___

■ 文字配列と文字列の相互変換

C#では、いかに容易に文字配列と文字列の間の相互変換ができるかを示す。
 {
   class Class1
   {
     static void Main(string[] args)
     {
          // char の配列
       char [] ar1 = { 'A', 'B', 'C' };
       foreach( char c in ar1 )
       {
         Console.WriteLine(c);
       }

          // string 型 Ctor に char[] を指定
       string s = new string( ar1 );
       Console.WriteLine(s);

          // char の配列型
       char [] ar2 = s.ToCharArray();
       foreach( char c in ar2 )
       {
         Console.WriteLine(c);
       }
     }
   }
 }
POINT char[], string の相互変換は簡単. char[] -> string : string( char [] ); string -> char[] : string.ToCharArray(); 最初に文字の配列の各要素が表示され、次に文字列に変換されたものが表示され、 最後に再び配列に戻されたものが表示されている。 string(System.String) のコンストラクタに文字(char)の配列を指定すれば、 その内容を文字列として生成する。 文字列に ToCharArray メソッドを使うと、戻り値として、文字の配列が返ってくる。  C#では、C/C++と違って、文字の配列と文字列はまったく別のものになる。 必要ならば相互に変換することは簡単にできる。 まとめ  C#の配列は、配列自身が1つのインスタンスである
___

■ Network



            Console.WriteLine( System.Net.Dns.GetHostName() );

            // IP を取得

            System.Net.IPHostEntry h =  System.Net.Dns.GetHostEntry("pc");

            Console.WriteLine(System.Net.Dns.GetHostName());
            Console.WriteLine( h.AddressList[0].ToString() );            
___

■ TcpClient

    using System.Net.Sockets;


            System.Net.Sockets.TcpClient c = new System.Net.Sockets.TcpClient();
            try
            {
                c.Connect("pc", 8080);
                NetworkStream s = c.GetStream();
                byte[] b = Encoding.ASCII.GetBytes( "GET / HTTP/1.0\r\n\r\n" );
                s.Write( b, 0, b.Length );

                byte[] rb = new byte[8 * 1024];
                s.Read(rb, 0, rb.Length);
                Console.WriteLine( Encoding.ASCII.GetString( rb ) );

                c.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("aaa");
            }
___

■ TcpListener

DESC TCPサーバ側の機能を提供するクラス。
    using System.Net.Sockets;
    using System.Net;

            // port番号 2000 でサーバーを開始する。
            TcpListener srv = new TcpListener( IPAddress.Any, 2000 );
            srv.Start();

            // クライアントの接続まちをする。
            TcpClient c = srv.AcceptTcpClient();

            // 接続できたら、相手の言ったことをstdoutへ出力する。
            NetworkStream st = c.GetStream();
            byte[] buf = new byte[1024];
            st.Read(buf, 0, 1024);

            string ret = Encoding.ASCII.GetString( buf, 0, 1024 );
            Console.WriteLine( "client msg = {0}", ret );


            // サーバをとめる。
            srv.Stop();
繰り返し対応をする。
        static private void ClientHandler(TcpClient c)
        {
            byte[] buf = new byte[1024];
            NetworkStream st = c.GetStream();

            // 相手が接続を切ると 0 が返る。
            while ( st.Read(buf, 0, 1024) != 0 ) {

                string ret = Encoding.ASCII.GetString( buf, 0, 1024 );
                Console.WriteLine( "client msg = {0}", ret );
                
            }
        }
上の処理では一人しか相手をできないので、 スレッドプールへ処理を投げる。
        {
            while (true)
            {  
                // メイン処理では客まちだけをして
                TcpClient c = srv.AcceptTcpClient();

                // 相手をするのは従業員にまかせる。
                ThreadPool.QueueUserWorkItem(new WaitCallback(ClientHandler), c);
            }            
        }



        static private void ClientHandler(Object o)
        {
            TcpClient c = ( TcpClient )o;
            byte[] buf = new byte[1024];
            NetworkStream st = c.GetStream();

            // 相手が接続を切ると 0 が返る。
            while ( st.Read(buf, 0, 1024) != 0 ) {

                string ret = Encoding.ASCII.GetString( buf, 0, 1024 );
                Console.WriteLine( "client msg = {0}", ret );
                
            }
        }
___

■ FileSystem(ファイルシステム)







  POINT
    たとえファイルが存在しても
    ファイルを読み取るのに十分なアクセス許可を持たない場合も、False
    Directory.Existsメソッドに指定するフォルダ名は
    パスの最後に"\"があってもなくても、"C:"のようであっても問題なし

    string f = @"C:\test.txt";
    if (System.IO.File.Exists(f))
    {
        MessageBox.Show( f + "'は存在します。");
    }
    else
    {
        MessageBox.Show( f + "'は存在しません");
    }    


    // copy
    System.IO.File.Copy("C:/src.txt", "C:/dst.txt" );

    // 上書き許可
    System.IO.File.Copy("C:/src.txt", "C:/dst.txt", true );

    using System.IO;

    // ファイルの有無
    if ( File.Exists("c:/foo") ) {}
    if ( Directory.Exists("c:/") ) {}

    // 作成、消去
    Directory.CreateDirectory("d:/test");
    Directory.Delete("d:/test");

    File.Delete( "d:/test.txt" );

    // 移動
    File.Move( "d:/src", "d:/foo/src" );

    // リネーム
    File.Move( "d:/src", "d:/dst" );
    
    
    // MyDocument
    Enviroment.GetFolderPath( Enviroment.SpecialFolder.Personal );

    // 列挙する。
    string[] a = Directory.GetDirectories( "d:/" );
    string[] a = Directory.GetFiles( "d:/" );


    // パターンにマッチしたファイルだけを返す
    string[] a = Directory.GetFiles( "d:/", "*.txt" );


    // パスの内容を解析 ( dirname )
    Path.GetRootPath( d:/foo.txt );    
    Path.GetDirectoryName( d:/foo.txt );    

    // foo.txt
    Path.GetFileName( d:/foo.txt );    

    // .txt
    Path.GetExtension( d:/foo.txt );    

    // foo
    Path.GetFileNameWithoutExtension( d:/foo.txt );
___

■ ファイル生成の監視


            FileSystemWatcher fs = new FileSystemWatcher();

            // 監視するディレクトリ
            fs.Path = "d:/";

            // 監視するファイル
            string file = "*.txt";
            fs.Filter = file;

            fs.NotifyFilter = NotifyFilters.FileName;

            string[] data = new string[] { "d:/", file };

            // 3秒後にスレッドにファイルを作らせる。
            if (ThreadPool.QueueUserWorkItem(new WaitCallback(func), data))
            {
                Console.WriteLine("wait");
                // 10 秒間だけ待つ
                WaitForChangedResult ret = fs.WaitForChanged(WatcherChangeTypes.Created, 10 * 1000);
                Console.WriteLine( "create file {0}", ret.Name );
            }

            Console.WriteLine("finish");
            
        }

        static void func( object obj )
        {
            string[] data = (string[])obj;
            Console.WriteLine("file をつくります");
            Thread.Sleep(3000);
            string path = data[0];
            string file = data[1];

            path += file;
            FileStream f = File.Create(path);
        }


___

■ TaskTray


  Icon Property を変更する


___

■ Sound


  ツールボックス上で右クリックメニューを出す
  [アイテムの選択]を実行
  COMコンポーネントタブを選択
  Windows Media Playerにチェックを入れる






 // コントロールの外観
 // none = 画面部分のみ。他にmini, fullがある。
 axWindowsMediaPlayer1.uiMode = "none";
 
 // ファイルの読み込み
 axWindowsMediaPlayer1.URL = "d:/test.mp3";

 // 自動再生
 axWindowsMediaPlayer1.settings.autoStart = true;

 // ボリューム
 axWindowsMediaPlayer1.settings.volume = 100;
 音量を0〜100で設定。
 OSの音量とは違うので、コントロールを複数同時に使って
 それぞれのボリュームコントロールが可能。

 // ミュート
 axWindowsMediaPlayer1.settings.mute = true;

 // 再生
 axWindowsMediaPlayer1.Ctlcontrols.play();

 // 一時停止
 axWindowsMediaPlayer1.Ctlcontrols.pause();

 // 停止
 axWindowsMediaPlayer1.Ctlcontrols.stop();

 // 閉じる
 axWindowsMediaPlayer1.close();
 ファイルを切り換える場合、閉じないと画面描画が残る。

 // シーク
 axWindowsMediaPlayer1.Ctlcontrols.currentPosition = 0;
 目的の秒数を指定する。double型なので秒数きっちりじゃなくてもOK。

 // フルスクリーン
 axWindowsMediaPlayer1.fullScreen = true;

 // 動画解像度の取得
 axWindowsMediaPlayer1.currentMedia.imageSourceWidth //幅
 axWindowsMediaPlayer1.currentMedia.imageSourceHeight //高さ

 // ステータスの取得
 axWindowsMediaPlayer1.status
 再生中、一時停止など、日本語の文字列が入る。
 WMVなどは詳細情報が加わって文字列変化するので要注意。

 // 再生時間の取得
 axWindowsMediaPlayer1.currentMedia.duration
 ※ファイルを読み込み、ステータスが"再生中"になると取得可能。

___

■ mp3

[System.Runtime.InteropServices.DllImport("winmm.dll",CharSet = System.Runtime.InteropServices.CharSet.Auto)] private static extern int mciSendString(string command, System.Text.StringBuilder buffer, int bufferSize, IntPtr hwndCallback); private string aliasName = "MediaFile"; //Button1のClickイベントハンドラ private void Button1_Click(object sender, EventArgs e) { //再生するファイル名 string fileName = "d:/work/pro/resrc/med/"; string cmd; //ファイルを開く cmd = "open \"" + fileName + "\" alias " + aliasName; if (mciSendString(cmd, null, 0, IntPtr.Zero) != 0) return; //再生する cmd = "play " + aliasName; mciSendString(cmd, null, 0, IntPtr.Zero); }
___

■ プロセス(Process)


___

■ 外部プロセスの実行

  using System.Diagnostics;
  using System.IO;

  // 外部プロセス ( Program Instance を起動する )

  Process ps = new Process();

  // 実行 File名
  // 引数がなければ 必須ではない
  ps.StartInfo.Arguments = "localhost";   


  // stdout を取得するには 次の2つの設定が必要
  //  プロセスの出力をリダイレクトする。
  ps.StartInfo.RedirectStandardOutput = true;
  //  Windowsのデフォルトシェルを利用しないようにする。
  ps.StartInfo.UseShellExecute = false;


  // 設定が完了したら実行開始
  ps.Start();  

  // 出力を読み取る
  string s = ps.StandardOutput.ReadToEnd();
  //WaitForExitはReadToEndの後である必要がある
  //(親プロセス、子プロセスでブロック防止のため)
  ps.WaitForExit();  

  /*
    Textbox に文字列を追加

      処理の実行におうじて, log を追加するには, ps の実行. text の追加を 別 thread でしないといけない
      window の描画 という処理が更新されないため
  */
  TextBox1.Text += s;


  // コンソールプログラムでもウィンドウ非表示に効果はある。
 {
    hPsInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;     //通常
    hPsInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;     //非表示
    hPsInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Minimized;  //最小化
    hPsInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Maximized;  //最大化

    ps.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;     //非表示


}

___

■ 実行中のプロセスを調べる


    // 現在の process ( 2 個目の場合 ) を取得する
    Process hThisProcess = Process.GetCurrentProcess();
    MessageBox.Show( hThisProcess.ProcessName );


    // process 名から listup する
    Process[] hProcesses = Process.GetProcessesByName(hThisProcess.ProcessName);


___

■ DLLの使用


    [DllImport("USER32.DLL", CharSet=CharSet.Auto)]
    private static extern bool SetForegroundWindow( System.IntPtr hWnd );
___

■ ネームスペース(Namespace)



 C#は「コンポーネント指向のプログラム言語」
  コンポーネント指向とは、すでに作成済みのクラスを持ち寄って、
  それを利用する形でコーディングを行うこと


  その際、まったく無関係に開発されたクラスを集めると、同じ名前のクラスが存在することがあり、
  名前の衝突が発生する.

  C#では、個々のクラスはより大きなnamespace(名前空間)に所属するものと考え、
  namespaceを区別することで
  同じ名前のクラスを使い分けることを可能とする。

  POINT
    namespace はネスト可能で . で区切ることができる。


  POINT
    namespace は複数にわけることもできる。
    これによってソースコードを分離することができる。

 namespace Space1
 {
   class Class1
   {
     public static void test(){}
   }
 }

 namespace Space1
 {
   class Class2
   {
     public static void test(){}
   }
 }
POINT 次にこの namespace 内の名前を使うときに完全修飾名を記述するのは手間なので using 宣言を利用することで 名前空間の指定を省略できる。 // namespace の省略があった場合に検索する namespace を指定する。 using System.IO; // 別名をつけることもできる。 using MyClass = Space1.Sub1.Class1; WARNING using は、クラスやメソッドの内部には記述できない。 namespace”宣言直後などに記述すること。
    namespace を有効にする. {} scope が考慮される. 

    {
      // Block 内のみ有効. System namespace を利用する
      using System;

      class Foo {}
    }
WARNING ネームスペース直下の 変数宣言はダメ。 必ずクラス内にいれること。
  namespace {
    Form1  pForm;
  }

___

■ namespace と using

namespaceやクラスの別名を宣言するusing  namespaceは便利な機能であるが 正面からこの機能を使うと、 何を指定するにもnamespaceの名前を使うことになり 表記が長くなる。 POINT ソースコード上の狭い範囲に着目すれば そこから参照するnamespaceの種類も限られている。 何も長い名前をいちいち書かなくても十分に分かりやすい ソースを記述できる。 これを支援するために C#にはusingという機能がある。 POINT usingには 別名とデフォルトnamespaceを記述する機能がある。 別名機能は、namespaceや、 それに属するクラスに対して、 1つの名前を宣言する機能を提供している。 “ultra.super.special.long.long.name”というような長い namespaceを頻繁に参照する場合に、もっと短い名前(例えば"longname)を付けることができる。
 namespace Space1.Sub1
 {
   class Class1
   {
     public static void test()
     {
       Console.WriteLine("Space1.Sub1.Class1.test() called");
     }
   }
 }

 {
      // 別名を与えた. 
   using MyClass = Space1.Sub1.Class1;
   using MySpace1 = Space1.Sub1;
   using MySpace2 = Space1;
   // using Class1 = Space1.Sub1.Class1; // 下のClass1と名前が同じになるのでエラー

      // 多重の using はできない. 
   // using MySpace3 = MySpace2.Sub1; // usingの対象にusingされたキーワードMySpace2は使用できないのでエラー
   class Class1
   {
     static void Main(string[] args)
     {
          // Space1.Sub1.Class1.test() と同じ.
       MyClass.test();
       MySpace1.Class1.test();
       MySpace2.Sub1.Class1.test();
     }
   }
 } 
___

■ ネストするnamespace

POINT namespace をネストするのと . 区切りにするのは意味としては同じ。 つまり、namespaceを宣言する際 ピリオド区切りの単一の名前で書いてもよいし、 ネストしたnamespaceとして宣言してもよい。
 namespace Space1.Sub1
 {
   class Class1
   {
     public static void test()
     {
       Console.WriteLine("Space1.Sub1.test() called");
     }
   }
 }
 namespace Space2
 {
   namespace Sub1
   {
     class Class1
     {
       public static void test()
       {
         Console.WriteLine("Space2.Sub1.test() called");
       }
     }
   }
 }
     {
     // 呼び出す表記は同じになる
       Space1.Sub1.Class1.test();
       Space2.Sub1.Class1.test();
     }
___

■ namespaceをわける。

POINT  namespaceの宣言は、 1つのブロックにまとめねばならないという制約はない 同じ名前のnamespaceの宣言が何回出現してもよい
 namespace Test
 {
   class Class1
   {
     static void Main(string[] args)
     {
       Space1.Class1.test();
       Space1.Class2.test();
     }
   }
 } 
同じ名前の複数の namespace にクラスを分けて記述できる。 POINT 1つのnamespaceの中に 同じ名前のクラスを2つ以上持つことはできない。 namespace をわけることは可能. Space1という名前のnamespaceを宣言している。 同じように、 同じSpace1というnamespaceを宣言している。 2つに分かれているが これは2つとも 同じ namespace の宣言の一部として機能する。 なぜなら、Space1というnamespaceの中では、 Class1という名前のクラスが宣言済みだからだ。 1つのnamespaceの中に 同じ名前のクラスを2つ以上持つことはできない。
___

■ namespaceの衝突

POINT: using は、複数を指定できる。 ただし衝突には注意すること。 複数のクラス・ライブラリに含まれている 多くのメソッドを、短い名前で呼び出すことができる。 WARNING 何でもかんでも using すればよいというものではない。 usingで指定された複数の namespace内に同じ名前のクラスがあり、 それを呼び出そうとするとコンパイラはエラーをだす。
 namespace Space1.Sub1
 {
   class Class2
   {
     public static void test()
     {
       Console.WriteLine("Space1.Sub1.Class2.test()");
     }
   }
 }

 namespace Space2.Sub1
 {
   class Class2
   {
     public static void test()
     {
       Console.WriteLine("Space2.Sub1.Class2.test()");
     }
   }
 }
{ // Space1.Sub1とSpace2.Sub1をusingで指定する。 // この2つのnamespaceに属するメソッドは // namespace名なしで呼び出すことができる。 using Space1.Sub1; using Space2.Sub1; class Class1 { static void Main(string[] args) { // Class2はあいまいな参照、というコンパイル・エラーになる // どちらの Class2 か判別できない。 Class2.test(); } } } エラーを解決するには呼び出したいクラスを明示する。
 {
   class Class2
   {
     public static void test()
     {
       Console.WriteLine("Space1.Sub1.Class2.test()");
     }
   }
 }

 namespace Space2.Sub1
 {
   class Class2
   {
     public static void test()
     {
       Console.WriteLine("Space2.Sub1.Class2.test()");
     }
   }
 }
___

■ 不完全なnamespaceとusing

usingはnamespaceのデフォルトを指定するが、 必ず完全なnamespaceでなければならない。 namespaceのネストの一部だけを指定できない。 namespace Space1.Sub1 { class Class2 { public static void test() { Console.WriteLine("Space1.Sub1.Class2.test()"); } } } { // Default namespace // Space1 という namespace がデフォルトであると宣言する。 // これで Space1 内のクラスは宣言可能になる。 // using Space1; class Class1 { static void Main(string[] args) { // Sub1 は namespace 名の一部を成す名前であって、 // 不完全なnamespace名を補うために宣言が使われることはない。 // Sub1.Class2.test(); // Sub1 が見つからないというコンパイル・エラーになる } } } エラーなくすには usingを使わずフルネームを書くか、 あるいは、usingで完全なnamespace名を指定する。 namespace ConsoleApplication23 { using Space1.Sub1; using Space2.Sub1; // 短い名前を別名として定義することで指定を簡略化する using MyClass2 = Space1.Sub1.Class2; class Class1 { static void Main(string[] args) { MyClass2.test(); } } } 2つの異なるnamespaceにある同じ名前のクラスの片方に別名をつけることにより アクセスするクラスを特定する。 POINT  namespace や using の機能は、短いプログラムを書いているときにはありがたみを感じられないかもしれない。 しかし、膨大なクラス・ライブラリを活用するために、 usingの機能は不可欠である。 そのようなライブラリを整備するためには namespaceは必須の機能。
___

■ FileDialog


    dlg.InitialDirectory = "c:/";

    // open する
    dlg.ShowDialog() {

    }

___

■ Event


  POINT
    イベント機能は、あるクラスで発生した出来事を
    あらかじめ登録された一群のメソッドに対して、
    1回の呼出しによって、ですべて伝えることができる。

  POINT
    Event はデレゲートという機能を使う。
    Windowアプリケーションで頻繁に使う。

     using System;

     namespace ConsoleApplication53
     {
              // Delegate 型の宣言. ( class の外でするらしい. )
       public delegate void SampleEventHandler(object sender, EventArgs e);

       class Class1
       {
                // Event の定義だよ-. ^ ^/

    //  これは、デレゲート(delegate)と呼ばれる機能を用いて
    //  イベントで用いるメソッドの引数を定義
        これは一種のデータ型の定義として機能する点に注意すること

        イベント本体には2つの機能がある
        ハンドラの追加/削除と
        イベントの発生


        // Event本体の定義
         public event SampleEventHandler sampleEvent;


        // ハンドラーとして機能するにはメソッドの引数と戻り値を同じにする。
         public void handler( object o, EventArgs e )
         {
           Console.WriteLine("handler called");
         }

         static void Main(string[] args)
         {
           Class1 o = new Class1();

                  // Handler( Listener ) の追加. ( -= で削除できる )
           o.sampleEvent += new SampleEventHandler( o.handler );

                  // Event を発生させる( 呼び出す )
                  // イベント名をメソッド名のように使う。
           target.sampleEvent( o, EventArgs.Empty );
         }

       }

     }
___

■ Type


  POINT
    data型には 値型と参照型の2種類がある。
    値型は変数にデータ自体がセットされるが、参照型はデータの場所が設定される。
    
    値型
      char, int, bool, struct, enum
    参照型
      object, string, array



  POINT
    ネイティブ型という概念がなく
    System.Int32 などの別名の型がある。

       string s = i.ToString();
               // (N) : N arg を埋め込む. 
       Console.WriteLine( "i={0}, s={1}", i, s );

  
  POINT
    変数は必ずデフォルト値で初期化される。
    ( C は不定 )
      初期化される値は基本的にゼロ
      浮動小数点型なら 0.0になる。bool型はfalse

     private static int i;
     public static int Main(string[] args)
     {
       Console.WriteLine( i );
       return 0;
     }
   }
    予約語  別名  意味
    sbyte  System.SByte  符号付き8bit整数
    byte  System.Byte  符号なし8bit整数
    short  System.Int16  符号付き16bit整数
    ushort  System.UInt16  符号なし16bit整数
    int  System.Int32  符号付き32bit整数
    uint  System.UInt32  符号なし32bit整数
    long  System.Int64  符号付き64bit整数
    ulong  System.UInt64  符号なし64bit整数
    char  System.Char    文字型
    float  System.Single  単精度実数
    double  System.Double  倍精度実数
    bool  System.Boolean  論理型(falseとtrueのみ)
    decimal  System.Decimal  10進型
    string  System.String  文字列型
数値の範囲はMinValue()と MaxValue() で取得できる。
    System.Int16.MaxValue();

    // 文字は UNICODE( utf16 )で実装しているため 0〜65535 
    
___

■ 参照


  DESC
    1. 変数は参照であり, 未初期化の変数は CMPERR になる. 


___

■ 値型と参照型 ( struct は 値型 class は参照型 )

System.Int32などを紹介してきたが、あえてクラスとは呼んでいなかった。 これは「Structs」と呼ばれるもの Structsは、Structsからの継承ができないなどの制約を除けば見かけ上クラスにそっくり POINT C#でのStructsとクラスの違いは それが値型なのか、参照型なのかという機能面での違い C#のstructは、C/C++ のstructとはまったくの別物 値型とは 情報を引き渡すときに情報をコピーする方式を取るデータ型 参照型とは データの実体がある場所を保存しておき 情報を引き渡す必要がある場合は データの本体をコピーせずデータの場所だけを伝えるという方式 この2つは、動作も微妙に違うが、それ以上に大きく違うのは、処理効率である 小さなデータなら問題ないのだが 大きなデータでは処理速度の低下という問題を招く 逆にデータが十分に小さいとすると、データの場所を渡すと言うような回りくどい方法を取るよりも データそのものをコピーする方が素早い、と言うこともある 主にデータ量という観点で使い分けること
   using System;

   public class Test
   {
     public int v;
   }
   public class Class1
   {
     public static int Main(string[] args)
     {
       // 時間を表示する. 
       Console.WriteLine( DateTime.Now );
       int count = 10000000;

       // 1000 万の Instance をつくる. 
       Test [] test = new Test[count];
     
       for( int i=0; i< count; i++ )
       {
         test[i] = new Test();
         test[i].v = i;
       }

       int sum = 0;
       for( int i=0; i< count; i++ )
       {
         sum += test[i].v;
       }
       Console.WriteLine( DateTime.Now );
       return 0;
     }
   }
具体的には、整数型変数を1つ含むクラスを宣言し、 このクラスを要素とする1000万個の配列を作って、要素番号を変数に代入する。 その後、全要素のクラス変数の値を加算していく。 5: int型変数を1つ含むTestクラスの宣言。 13: 「DateTime.Now」は現在時刻を返す。 プログラムが実行を開始されたら、まずはこのプロパティを表示し、処理を開始する直前の時刻を表記する。 14: 今回宣言する配列の要素数を保持する変数。今回は1000万とした。 15: 1000万個の要素を持つTestクラスの配列を作成し、変数「test」に代入する。 16〜20: test配列の各要素となるTestクラスを作成し、そのインスタンス変数「v」にカウンタの値を代入する。 21: 計算処理に使用する整数型変数。ただし今回は計算処理の実行性能を調べるのが目的なので、計算の内容自体には意味はない。 22〜25: test配列の各要素であるTestオブジェクトのv変数の値を次々と加算し、結果を変数「sum」に代入していく。 26: 処理終了の時刻を表示する。 POINT 13行目と26行目の DateTime.Now は、現在時刻を返すプロパティである。 これを使って、処理前と処理後の時刻を表示させ
 {
   using System;

   // struct を利用する.   
   public struct Test
   {
     public int v;
   }
   public class Class1
   {
     public static int Main(string[] args)
     {
       Console.WriteLine( DateTime.Now );
       int count = 10000000;
               
       Test [] test = new Test[count];  // ここで巨大な Block を確保している. 
       for( int i=0; i< count; i++ )
       {
                 // struct ならば, ここも不要. 
         test[i] = new Test();
         test[i].v = i;
       }
       int sum = 0;
       for( int i=0; i< count; i++ )
       {
         sum += test[i].v;
       }
       Console.WriteLine( DateTime.Now );
       return 0;
     }
   }
 }
class // クラスの場合、1個のインスタンスは、1つの独立したメモリ領域として確保される。 [ ref ] --> [ Instance ] [ ref ] [ ref ] Struct [ Instance ] [ Instance ] [ Instance ] クラスとStructsでの確保されるメモリ領域の違い クラスを用いた場合には(図[上]) 各インスタンスごとに独立したメモリ領域が確保され 値が格納される 配列の各要素はそれぞれのメモリ領域への参照を保持する Structsを用いた場合 配列を作成した時点で、要素数分のStructsを納めるための巨大なメモリ領域が確保され その中に値が格納される 反面、Structsの利用は危険も伴う。Structsは、制限の厳しいクラスのようなものだが、 クラスそのものではない。 クラスと思い込んで扱ってしまうと、思わぬトラブルが起きる場合もある。 例えば、巨大なStructsを作成してしまうと、Structsは値型なので、別の変数に代入する場合などには、 丸ごと中身をコピーする羽目になり、処理効率を著しく落とす。 Structsはツボにはまれば効率アップできるが、 一歩間違えると処理効率を大きく下げかねない危険がある。
___

■ Statement


  POINT
   古い世代のプログラム言語では
    すべての機能を言語仕様に取り込んでしまうことが多かった
    入出力などの機能は言語の一部ではなく、ライブラリに置く方向に進化してきた

    ステートメントとして残ったものは、ライブラリでは実現できない最小限度のものに限られている。


  ブロック

   C#で最も基本となるステートメントがブロック
    ブロックは複数のステートメントを集めて1つのステートメントであるかのように見せかける機能を持つ

    1つのステートメントに作用する機能に対して、
    複数のステートメントを与えることができる。

 ブロック・ステートメントの構文は {}
 ブロック・ステートメントはローカル変数の寿命に影響を与える

  POINT
   ブロックは
    変数がメソッド内のある特定の範囲でしか使われないことを明示的に示すためにも使われる。つまり
    ブロック外から参照することが無意味な変数を誤って参照すれば
    コンパイル・エラーでわかる。

  POINT
    ローカル変数の寿命はブロックの範囲内に限定される。 


___

■ if

___

■ switch

 C#のswitchステートメントでは、文字列(string)も使うことができる。 switchステートメントがその実例を示したものだ。 WARNING C#はbreakまたはgotoを用いないで、次のcaseの処理にそのまま入って行くことは許されない “goto case”を用いて、明示的に次に処理するべき先を指定しなければならない。 POINT foreachでは、ある値が何番目であるかは、重要な意味を持たない。 foreachは配列だけでなく、データの集まりを扱う多くのクラス( Collection )に適用できる。 自作クラスでも、foreachを適用可能にすることができる(IEnumerableを実装することで実現できるが、
___

■ GUI


    Form とは, GUI をはりつける部品の領域のこと.
    System.Windows.Forms.Form




    
  USAGE
    File > New > C# WindowsApplication

    コントロールはツールボックスから配置する。
    表示 > ツールボックス
d:/img/test/test.bmp File 構成 Form1_Designer.cs // Form1 class が定義されている. Form 用の code ( 自動生成 ) Program.cs // EntryPoint 新たにフォームを作る SE > NodeApp > RMB > 「追加」→「Windowsフォームの追加」
___

■ DialogBox

    // どのボタンを選択したかを判断する
    if (MessageBox.Show("ボタンを選択", "タイトル", MessageBoxButtons.YesNo) == DialogResult.Yes) {
        MessageBox.Show("[はい] ボタンを選択しました");
    } else {
        MessageBox.Show("[いいえ] ボタンを選択しました");
    }

___

■ Event

___

■ Mouse(マウスイベント)

  // マウスボタンの左ボタンが押されたときにウィンドウを移動する。
        private void splitContainer1_Panel1_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                this.Left += e.X;
                this.Top += e.Y;
            }
        }
___

■ KeyboardEvent(キーボードイベント)

___

■ Form

using System.Windows.Forms;

    // Icon

    // タイトル
    Form.Text = "タイトル";

    // 透明
    Form.Opactiy = 0.5f;

    // 最前面
    Form.TopMost = true;

    // 最大, 最小ボタン
    Form.MaximizeBox = false;
    Form.MinimizeBox = false;

    // 背景画像
    Form.backgroundImage = Image.FromFile( "d:/test.bmp" );


    // ウィンドウ枠( フレーム )をなしにする。
    Form.FormBorderStyle = FormBorderStyle.None;
    Form.TransparencyKey = Color.White;

    // 表示する。
    Form.Show();
    
    // 閉じる
    Form.Close();

    // 非表示にする。
    Form.Hide();

    // フォームを強制的にアクティブにする
    //   複数のフォームやマルチタスクによりフォーカスが他のフォームに移る場合に使う。
    this.Activate();
Show メソッドは生成元のオブジェクトが残る。 Form1とForm2が同時 に表示され 同時にフォーム上のコントロールを操作できる。
           private void Form1_Load(object sender, System.EventArgs e)
           {
              Form Form2=new Form();
              Form2.Show();
           }
ShowDialog メソッドは生成元のオブジェクトからフォーカスを奪うとともに 生成元を閉じる。
        private void Form1_Load(object sender, System.EventArgs e)
        {
           Form Form2=new Form();
           Form2.ShowDialog();
        }    

   ShowDialogメソッドで開いたフォームの場合
   Closeメソッドを実行してもフォームオブジェクトは捨てられないので
   Disposeメソッドを併せて実行すること。
   (Disposeすることによって使用メモリが開放される。)

    this.Dispose();

フォームを画面中央に表示するには 画面全体の大きさを取得する。
    int w, h;
    w = Screen.PrimaryScreen.WorkingArea.Width; //画面の幅
    h = Screen.PrimaryScreen.WorkingArea.Height; //画面の高さ
  // Form の表示前
  private void Form_load( object sender, EventArgs e ) {
  
  }
  // Form の閉じる前
  private void Form_FormClosing( object sender, EventArgs e ) {
  
  }
  // Form の閉じた後
  private void Form_Closed( object sender, EventArgs e ) {
  
  }
  // Form のサイズが変わったとき
  private void Form_Resize( object sender, EventArgs e ) {
  
  }
___

■ DragDrop

まず DragDrop をうけつけるように form の AllowDrop プロパティを true にする。
    DragDrop = true
    // DragDrop をうけつける

    プロパティエディタで "イベント""DragDrop""DragEnter" に用意する。

    // "DragEnter" のイベントハンドラー内で、ドラッグされてきたファイルを受け入れられることを通知するプログラムを記載します。

    private void dataGridMeisaisyo_DragEnter(object sender, DragEventArgs e)
    {

      // ファイルのドラッグアンドドロップのみを受け付けるようにする。
      if (e.Data.GetDataPresent(DataFormats.FileDrop))
      {
          // ドロップされたファイルは、アプリケーション側に内容がコピーされる
          e.Effect = DragDropEffects.Copy;

      }

    }    

    // "DragDrop" イベントハンドラーに、ドロップされた時の制御プログラムをかく
    private void dataGridMeisaisyo_DragDrop(object sender, DragEventArgs e)
    {

        // ドラッグアンドドロップされたファイルのパス情報を取得します。
        foreach (String path in (string[])e.Data.GetData(DataFormats.FileDrop) )
        {
          // "filename" 変数に格納されたドロップされたファイルの処理をする。

        }

    }
___

■ Control

    // 表示, 非表示
    Control.Visible = true;

    // 有効/無効
    Control.Enabled = true;

    // 位置
    Control.Left = 0;
    Control.Top = 0;

    // 大きさ
    Control.Width = 100;
    Control.Hight = 100;

    // 自身にフォーカスする
    Control.Focus();

___

■ Control.の動的な追加

    Form.Controls.Add( Control )

    Form_load() {
      Label l = new Label();

      this.Controls.Add( l );
      this.Controls.Remove( l );
    }

___

■ layout

___

■ SplitContainer

DESC Control がない状態でフォームに配置すると 縦に分割した2つの領域を作成できる。
___

■ Textbox

{ this.LogWindow.Clear (); // 文字列を追加 LogWindow. // Accept.cs 参照
    textbox1.Text = "aaa";
    textbox1.Clear();

    // ReadOnly にする
    textbox1.ReadOnly = true;

    // 選択された文字列
    textbox1.SelectedText;
    // 選択された文字数
    textbox1.SelectionLength;

    // 行数( string 配列 )
    textbox1.Lines.Length;

    // 追加する。
    textbox1.AppendText ( str + "\r\n" );
    
    MessageBox.Show( textbox1.Text );
___

■ CheckBox


    if ( CheckBox1.Checked() ) {


    }
___

■ Listbox

    // 項目を追加
    listBox1.Items.Add( "A" );
    listBox1.Items.Add( "B" );

    // 削除
    listBox1.Items.Remove( "B" );
    listBox1.Items.RemoveAt( 2 );
    listBox1.Items.Clear()

    Object o = listBox1.SelectionItem;
    int i = listBox1.SelectedIndex;

___

■ CommboBox

    // 項目を追加
    commboBox1.Items.Add( "A" );
    commboBox1.Items.Add( "B" );

    // 選択中のアイテム
    commboBox1.Text;
___

■ ProgressBar

                          //progressBar1.Maximum = 100;
                            //progressBar1.Value += 10;
                            //progressBar1.Value += 50;
                            progressBar1.Value += 33;

  WARNING
    Value が 100 を超えると, 落ちる
    query すること

    // windows の style の影響をうける
            //progressBar1.Style = ProgressBarStyle.Marquee;
            progressBar1.Style = ProgressBarStyle.Continuous;
___

■ PictureBox

    // 画像を設定
    pictureBox1.Image = Image.FromFile( "d:/foo.bmp" );

    // 大きさの調整
    pictureBox1.SizeMode = PictureBoxSizeMode.AutoSize;
___

■ Thread


___

■ thread

  using System.Threading;

  // msec 単位で指定する
  Thread.Sleep( 1000 );

  // 

  BeginInvoke



    C# なら、Thread クラスとデリゲートで並行処理・並列処理もお手軽。 
    実際には、スレッドを直接作らず、スレッド プールを介して使う

    排他処理は lock 文で ( 文という扱いらしい )
___

■ Thread.クラスを使う。

WARNING Threadクラスを使用して別スレッドで動作させることができるメソッドは 戻り値がvoidで、パラメータを取らないメソッドのみ POINT スタティックなメソッドは Win32 スレッドや pthread のように 汎用型をひとつとる メソッドを実行できる。
    Thread.Start();
    Thread.Start( Object );
___

■ Delegate.を使う

POINT  スレッドとして動作させたいメソッドと同じ引数を持つデリゲートを作成 BeginInvoke() を呼ぶ それだけでそのメソッドは別スレッドで実行される BeginInvoke で nullが2つ余計に指定されているのは、 スレッドの終了をチェックするためのもの C#では、スレッドの完了をチェックする仕組みまで言語レベルで提供される



___

■ Lock.する。(排他処理)

C# には lock 文と言う排他制御のための専用の構文がある。 lock 文は以下のようにして使う lock 文を使うと コンパイラが自動的に Monitor クラスを用いた排他制御用のコードを 生成してくれる。
    lock( 同期オブジェクト )
    {
      クリティカルセクション
    }

    // 鍵をかけてから処理をする。

    lock( syncObject )
    {
       int tmp = num;
       Thread.Sleep(1);
       num = tmp + 1;
    }     
___

■ Thread.間に同期をとる。

POINT Eventオブジェクトを使い、スレッド間で同期をとる。


___

■ 有限のリソースにアクセスする。(空きがでるまで待つ。)

___

■ ThreadPool.スレッドプール

POINT スレッドプールを使うと自分でスレッドを生成しなくても システム側で勝手に順次処理を実行してくれる。
namespace System.Threading
{
    public delegate void WaitCallback(object state);
}
使う側
    static void method( object o ); 

    class Test {
      public void method( object o ); 
    }
___

■ 非同期メソッドの完了通知をうけとる。

POINT こちらから処理の終了をまったり、聞かないで 呼んでもらう。
    class Program
    {

        public delegate int AsyncInvoke();

        // 非同期で実行するメソッド
        static int Method1() {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine( "洗濯をします。" );
            Thread.Sleep( 1000 );
            return 0;
        }


        // 非同期の処理が完了したら呼んでもらう callback メソッド
        static void callback( IAsyncResult iret ) {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine( "おわったみたいだから、洗濯をほします。" );
        }
        


        static void Main(string[] args) // aaa
        {

            Console.WriteLine("aaa");

            AsyncCallback cb = callback;
            
            AsyncInvoke ai = Method1;

            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("洗濯をしといて、終わったら呼んでね。");
            ai.BeginInvoke(cb, ai);


            // 非同期で実行したらまつ
            Thread.Sleep( 5000 );

            Console.WriteLine("終了");
        }
    }

___

■ Date(日付)


  DateTime 構造体を使う


    // 現在時刻
    DateTime.Now.ToString();

    // 今日
    DateTime.Today.ToString();

    // 日付をしてインスタンス作成
    DateTime t = new DateTime( 2011,11,27,12,10 );

    // 時間などを個別に取り出す ( int 型 ) 
    t.Year;
    t.Month;
    t.Day;
    t.Hour;
    t.Minute;
    t.Second;

    // 曜日
    if ( t.DayOfWeek == DayOfWeek.Sunday ) {}

    // 時間を進める
    t.AddDays(1);

    // 経過時間
    s = DateTime.Now;
    Thread.Sleep( 3000 );
    e = DateTime.Now;
    
    TimeSpan t = e - s;
    t.TotalSeconds;

___

■ Exception(例外)



    // 任意のコードブロック単位で設定できる。
    try {
    
    }
    catch ( e ) {

    }

C++ との互換性のため 引数なしの catch ブロックもできる。
    try {

    }
    catch {
      // ただし例外の詳細な情報はとれない。

    }


    try {

      // 例外を意図的になげる
      throw new Exception( "test" );

    }
    catch ( Exception e ) {

      // エラー内容と種類
      Console.WriteLine( e.Message() );
      Console.WriteLine( e.GetType() );
    }
    finally {

    }
___

■ 例外の種類

Java と同じく、例外にもクラス階層があり、詳細にエラー処理をする場合は 派生先の例外クラスを指定する。 任意の例外をキャッチするなら、 Exception を指定する。
    try {

    }
    catch ( IOException ) {

    }
    catch ( InvalidOperationException ) {

    }
___

■ 例外の再スロー


    try {

    }
    catch ( Exception e ) {
      // 何かの処理をして、

      // 再度なげる
      throw;
    }


___

■ 非同期Delegateの例外

    class Test
    {
        public static void method() {

            Thread.Sleep(3000);
            Console.Write("例外を発生させます");

            int z = 0;
            int i = 10 / z;
        }
    }

    {
        public delegate void AsyncInvoke(); 

        static void Main(string[] args)
        {
            AsyncInvoke i = new AsyncInvoke( Test.method );
            IAsyncResult ret = i.BeginInvoke( null, null );

            while (ret.IsCompleted == false )
            {
                Thread.Sleep( 100 );
                Console.Write(".");
            }

            // EndInvoke() を実行することで, 例外が補足できる。
            // 逆に実行をしないと補足できないため、返値の有無に関係なくコールすること。
            try
            {
                i.EndInvoke(ret);
            }
            catch (Exception e)
            {
                Console.WriteLine("Thread throw Exception");
            }
         }
    }

___

■ プロパティ( Accessor )


  [attributes] [modifiers] type identifier {accessor-declaration}


   要は, VarMem 名を指定するだけで, 値を参照. セットできる. 



    プロパティとは、暗黙のアクセッサメソッド。
    通常のメンバにアクセスするように見せかけ、内部ではメソッドを通してくれる。

    そのクラスを利用するプログラマはアクセッサメソッドを意識せずにメンバにアクセスできる。

    accessor-declaration には、プロパティのアクセッサを宣言する。
    このアクセッサがプロパティの本体であり、暗黙のアクセッサメソッドとなる。

    アクセッサに対する代入行為があった場合、暗黙的に set アクセッサが呼ばれる。
    set アクセッサは、void 型の戻値でプロパティ型のパラメータを一つ持っているメソッドと同じ。
    代入された暗黙的な値は、常に value という名前を持つ。
    
    代入以外の形でプロパティが参照された場合は get アクセッサが呼び出されます
    get アクセッサはプロパティ方の戻りとを持つ引数なしのメソッドと同じ。    

  POINT
    MSIL レベルでは「プロパティ」という概念は存在しない。
    プロパティはコンパイル時にアクセッサメソッドに変換されている。

    プロパティをオーバーライドするということも可能
    ただし、この時はアクセッサレベルではなくプロパティレベルでオーバーライドされるので
    set アクセッサをオーバーライドした場合は、get アクセッサもオーバーライドされます    

    メンバ変数を変更されたくないということもあるでしょう
    set アクセッサを省略することで読み取り専用プロパティを作れます
    

class StringTest {

  private string strName;

  // [Strname] が set/get する際の名前.
  public string StrName {
    get {
      System.Console.WriteLine("strName を要求");
      return strName;
    }
    set {
      System.Console.WriteLine(value + "を設定");
      strName = value;
    }
  }
}

class Client {
  static void Main() {
    Kitty obj = new StringTest();
    obj.StrName = "test";
    string str = obj.StrName;
  }
}  


___

■ Indexer.Property


  POINT
    インデクサとプロパティはコードの読みやすく簡潔に書けるようにする目的で使う言語機能。
    メソッドとして実装する内容をインデクサ、プロパティで書ける。

    プログラムを用意する側も利用する側もシンプルにかけて、読みやすい。


    ただし、メソッド内でする動作はインデクサでは添え字のように振る舞い、
    プロパティはメンバ変数への読み書きをするようにする。

    プロパティはメンバ変数であるかのようにメソッドを記述できる。
    クラスのメンバ変数であるかのようにアクセス可能でありながら、
    処理の内容を自分で記述できる機能

    インデクサが配列であるかのようにアクセスできる機能

    Property は変数の[ ふ.り ]をする.


  POINT
      内部データと Interface 型を一致させる必要はない。
      Interface を変更せずに実装を変更する時に便利。
      内部で保持するデータ形式と、外部に見せるデータ形式が一致しないときに、
      そのギャップを埋めるのがプロパティの使い方の1つ


  プロパティと+=演算子
  POINT
   +=演算子が有効に機能するデータ型のプロパティであれば、
    プロパティに += 演算子を使うことができる。

      system.get_somethig().get_hoge(index)


      // 簡潔にかけて読みやすい。
      system.somethig[index]
.NET 3.0 からはメンバ変数の定義の省略ができる。 set, get を記述するだけ
    public int Count {
      set; get;
    }
WARNING プロパティの意味に矛盾する実装はしないこと。
   class Sum
   {
     private int sum = 0;
     public int Add
     {
       set { sum += value; }
     }
     public int Result
     {
       get { return sum; }
     }
   }

       Sum sum = new Sum();

        // 代入ではなく足し算が実行される
       sum.Add = 1;
     private int sum = 0;
    // Property 
     public int Value
     {
       set { sum = value; }
       get { return sum; }
     }

       Sum sum2 = new Sum();
       sum2.Value += 1;
       sum2.Value += 2;

    // インデクサの添え字を足し算に使用しており
    // 定義を全て読まないと挙動がわからない。
     public int this[int index]
     {
       get
       {
         return a + index;
       }
       set
       {
         a = index + value;
       }
     }
        // 内部データと interface となる型を一致させる必要はなし. 
     private int number;
     public string Order
     {
       get {
         switch( number )
         {
           case 1:
             return "one";
           case 2:
             return "two";
           case 3:
             return "three";
           default:
             return "unknown";
         }
       }
       set {
         switch( value )
         {
           case "one":
             number = 1;
             break;
           case "two":
             number = 2;
             break;
           case "three":
             number = 3;
             break;
           default:
             number = -1;
             break;
         }
       }
     }
___

■ read/writeを制限する

POINT 読み出し専用インデクサ  インデクサを定義する場合はgetとsetの2つの内容を記述するのが普通だが、 getだけ、あるいはsetだけを記述して読み出し専用、書き込み専用のインデクサを作れる。 読み出し専用インデクサへの書き込みはコンパイル・エラーとなる。
   class Class2
   {
        // 宣言 + 初期化.  
     private char [] a = { 'A', 'B', 'C' };

        // Indexer の構文: char を返す.
        // Type this[ Type Param ]
        // インデクサの宣言は名前には this を入れて、引数は角括弧[]でくくる。
     public char this[int index]
     {
     // get、set、valueのキーワードを用いて記述する。

     // 読み出し時の処理を記述する。
       get
       {
         return a[index];
       }
       set
       {
         a[index] = value;  // 暗黙パラメータ
       }

     }
   }
   class Class1
   {
     static void Main(string[] args)
     {
       Class2 t = new Class2();

       for( int i=0; i< 3; i++ )
       {
         Console.WriteLine( t[i] );
       }
       t[0] = 'X';
       t[1] = 'Y';
       t[2] = 'Z';

       for( int i=0; i< 3; i++ )
       {
         Console.WriteLine( t[i] );
       }

     }
   }


POINT インデクサによって配列のようにアクセスできるクラスがあるからと言って、 そのクラスが配列の持つ全機能を使えるわけではない。
   class Class1
   {
     static void Main(string[] args)
     {
       string s = "Hello!";

       char [] a = { 'H', 'e', 'l', 'l', 'o', '!' };
       Console.WriteLine( a.GetUpperBound(0) );
       //Console.WriteLine( s.GetUpperBound(0) );  // 'string' に 'GetUpperBound' の定義がありません。
       Console.WriteLine( s.ToUpper() );
       //Console.WriteLine( a.ToUpper() ); // 'System.Array' に 'ToUpper' の定義がありません。

          // Type を取得する. 
          // System.String
       Console.WriteLine( s.GetType().FullName );
          // System.Char[] 型
       Console.WriteLine( a.GetType().FullName );

     }
   }


POINT   インデクサの角括弧[]の内側に記述する値は整数とは限らない インデクサを使用しているHashtableクラスでは連想配列が可能になる。 Hashtableクラスのインデクサは、読み書きする値も、添え字も、object 型として定義されている。

   using System;
   using System.Collections;

   class Class1
   {
     static void Main(string[] args)
     {
       Hashtable h = new Hashtable();

       h["斉藤"] = "Windows 2000";
       h["田中"] = "Windows 98";
       h["鈴木"] = "FreeBSD";

       Console.WriteLine( h["斉藤"] );
       Console.WriteLine( h["田中"] );
       Console.WriteLine( h["鈴木"] );
     }
   }

[attributes] [modifiers] indexer-declarator {accessor-declarations} DESC インデクサを使うと配列でない要素に添字を使用できる。 使いどころとしては簡単なリストを作成し、添字を用いてアクセスしてみる。 ベクトルのように内部で配列を扱うオブジェクトは 内部の配列とオブジェクトの操作をインデクサによって通信させるという方法をとる。 こうすれば メソッドの仕様を覚える必要なく、クラスの利用者は直感的にプログラムできる。 // こんな感じになるらしい. T this[int id] { get ; set ; } オブジェクトを配列のように扱う方法 この配列にアクセスする手段として、インデクサを採用するという設計をとる. indexer-declarator type this [formal-index-parameter-list] インデクサの機能はプロパティの仕様に類似している。 配列のようなオブジェクト操作を暗黙的にメソッドに変換する機能がインデクサ。 インデクサを使いこなすには、一定以上のオブジェクト指向の「設計」経験が必要。 プロパティは名前で識別されるが インデクサはシグネチャで識別される。
public class list
{
   private static int item = 1;
   private object value;
   private list next;

   public list( object o ){
      value = o;
      next = null;
   }

      // index を指定して取得
   public object get( int index ){
      if( index > item - 1 )
         return "index error";

      list l = this;
      while( index != 0 ){
         l = l.next;
         index--;
      }
      return l.value;
   }

      // index を指定してセット
   public void set( int index, object value ){

      list l = this;
      while( index != 0 ){
         l = l.next;
         index--;
      }
      l.value = value;
   }

      // Indexer 
      // 要は Property の [ index ] version 
   public object this[int index]{
            // 取得する場合は 
      get{ return this.get(index); }
            // セットする場合は,                  
      set{ this.set( index, value ); }
   }

  // 要素を追加
   public void add( object o ){
      list l = this;
      while( l.next != null ) {
         l = l.next;
            }
      l.next = new list( o );
      item++;
   }

   public void show(){
      list l = this;
      Console.WriteLine( "item : {0}", item );
      do{
         Console.WriteLine( l.value );
         l = l.next;
      }while( l != null );
   }
}
{
   public static void Main(){
      list l = new list( "item1" );
      l.add( "item2" );
      l.add( 300 );
      l.show();

      object o = l[0];
      l[0] = l[1];
      l[1] = o;
      l.show();

      Console.WriteLine( l[2] );
      Console.WriteLine( l[3] );
   }
}
___

■ constructor.Destructor


  クラス内に変数などがある場合、インスタンスを生成してから変数を利用するまでの間に、
  適切な値を初期値として設定したい場合がある。

  POINT
    コンストラクタは
    インスタンスが生成された瞬間に実行されるメソッドのひとつ。
    コンストラクタの名前は、クラス名と一致させねばならない。
    コンストラクタは暗黙のうちに実行されるため、戻り値は存在しない。
    newによって生成された時点で自動的に呼び出されている。

    コンストラクタもオーバーロード可能なので
    引数の異なる複数のコンストラクタを1つのクラスに持たせることができる

    共通の初期化コードなどがあった場合、このような仕様は不便である。
“: this(…)”により
    別のコンストラクタを呼び出すコンストラクタを使用する。

   class Class2
   {
     public Class2()
     {
       Console.WriteLine("public Class2() called");
     }

        // このコンストラクタの実行に先立って同じクラス内で引数なしのコンストラクタを実行してね ^ ^/ という意味.
     public Class2( int x ) : this()
     {
       Console.WriteLine("public Class2( int x ) called");
     }

     public Class2( string s ) : this(1)
     {
       Console.WriteLine("public Class2( string s ) called");
     }
   }

   class ClassBase
   {
     public int x1,y1;

         // base の Ctor 
     public ClassBase( int x, int y )
     {
       x1 = x;
       y1 = y;
     }
   }
   class ClassDelived : ClassBase
   {
     public int x2,y2;

     // 継承元を呼び出す
     public ClassDelived( int x, int y ) : base( x+1, y+1 )
     {
       x2 = x;
       y2 = y;
     }
   }

___

■ メンバ変数の初期化

 クラス内のメンバ変数に初期値を入れるだけなら、コンストラクタを使うのではなく、 直接初期値を指定できる。 クラスが生成されるときにその値が自動的に代入される。
    public class Test {

      // x の初期値を 1 にする。
      public int x = 1;

      public static void Main() {
        Test t;
        Console.WriteLine( t.x );
      }
    }
POINT コンストラクタは メンバ変数の初期値が設定された後に実行される。
      // x = 1 の初期化の後に実行される。

      Test() {
        // x = 1 から 10 に変更する。
        x = 10;
      }
___

■ private.Constructor

POINT private な Ctor は new を封印する.( 逆に public を忘れずに. ) private なコンストラクタ 結論を先取りすると、コンストラクタは外部からいっさい呼び出し不可能になる。 、new を使ったインスタンスの生成がいっさい不可能になるというのに等しい。 staticなメソッドだけを提供するクラスを作ったとしよう このクラスのインスタンスを生成することは無意味である だが、勘違いしたプログラマが インスタンスを生成しようと試みるかもしれない
   class Class2
   {
     private Class2() {}
        // 
    }

___

■ static なコンストラクタ

 C#には、staticなメソッドやメンバ変数が存在するが、 コンストラクタにもstaticなものが存在する。staticなコンストラクタは、 そのクラスのインスタンスが生成されたり staticメソッドなどが呼び出されたりする前に自動的に呼び出されるコンストラクタである ただし、いつ呼び出されるかは厳密に規定されていないので プログラムの実行開始時に呼び出されるという保証はない。  staticなメソッドやstaticなメンバ変数をいろいろ活用するとき、 事前にメンバ変数を初期化したいときに便利な機能 インスタンスを生成していないため呼ばれないはずのコンストラクタだが、 static を指定することによりそれが実行されているのが分かる。
   class Class2
   {
        // static な Ctor 

  staticなコンストラクタには、publicやprivateのようなアクセス制御キーワードは付けない。
  これは暗黙のうちに実行する以外の選択肢がないので、
  それを明示的にコントロールする意味がないためだ。
     static Class2()
     {
       Console.WriteLine("static Class2() called");
     }

     static public void special()
     {
       Console.WriteLine("static public special() called");
     }
   }

___

■ delegate


  SYNTAX
    [attributes] [modifiers] delegate result-type type ([formal-parameters]);

  DESC
    関数ポインタは型保証がなく、リスクが生じるのでオブジェクト指向には適さない
    そこで、C# ではデリゲートという機能がつかう

    
    デリゲートの宣言は名前空間やクラスのメンバ空間などで宣言できる。
    クラスのメンバ空間で宣言した場合も、インスタンスとの関わりはない。

    デリゲート型とは System.Delegate の派生クラスを指す。
    重要なことですが、Delegate クラスはデリゲート型ではない。
    Delegate クラスの派生クラスがデリゲート型になる。
    デリゲート型は 暗黙的に sealed となっている。
    public abstract class Delegate : ICloneable, ISerializable
デリゲートする関数はシグネチャが同じなら、インスタンスのメソッドでもよい。 この時、デリゲートはインスタンスの情報と共にメソッドの情報を格納する。 コールバックの仕様などにあわせて、目的のシグネチャを指定できます // delegate 型をつくる. // delegate の Instance を作成することで使用する. new delegate_type( expression // delegation で呼びだす Method ); // expression に指定するメソッドは、必ずデリゲート型と同じシグネチャでなければなりません // シグネチャが同じであれば、型が保証されるのでほかに制約されるものはありません デリゲートを用いてメソッドにアクセスする デリゲートの識別子と () を用いて、通常のメソッドのように呼び出すことができる
     class Test {
       static void Main() {

         // Delegator を作成して,  void、パラメータ無しのメソッドの位置を保有する。
         TestCallback test = new TestCallback(Test);

         // callback を呼ぶ。
         test();
       }
       static void Test() {
         System.Console.WriteLine("Test on your lap");
       }
     }
delegateとは何か POINT 関数ポインターのこと。 委譲という言葉が分かりにくいと思うなら、「代表者」と考えてもよい。 何かの処理を実行させたいときに、直接処理機能を持つメソッドを呼び出すのではなく、 代表者に処理を求めるのである。代表者は処理機能自体は持っていないが、 それを処理できる適切なメソッドを知っていてそのメソッドに処理要求を渡す。 EventHandler pFoo; といいかえれば OK. 「 クラスA 」の「 メソッドa 」は自分では何も処理せず、 常に「 クラスB 」の「 メソッドb 」に処理をゆだねるとしよう。 このような場合、「 メソッドa 」の中に「 メソッドb 」を呼び出すコードを書けばよいので、 特別な機能は何も必要がない。 A::methodA() { // ここを固定したくない B::methodB(); } POINT このように常に処理をゆだねる相手が決まっているとは限らないし、 相手が常に1つとも限らない この状況に対応するために delegate を使う。 WARNING delegateの機能はeventの機能によく似ている 実際に、event機能はdelegate機能を利用して作られている。 delegateとeventは想定される使われ方が違っており 機能面でも違いがある。 delegateは処理を他にゆだねるものだが eventはクラス内の出来事を外部に伝えるもの。 どちらか一方だけ覚えて 他方は使わないという方法はうまくいかない。 関数ポインタと似ているのは役割であって 関数ポインタの機能とdelegateの機能が同じではない。 C言語で関数ポインタを使っていた場面で、 C#ではdelegateを使うかもしれないが 具体的な機能はまったく違うということ // delegate経由でメソッドを呼び出すサンプル using System; namespace ConsoleApplication20 { // delegate 型だよ - ^ ^/ // Signature は int ( int, int ); だよ. ^ ^/ // そして 名前は Sample だよ. ^ ^/ // delegate のデータ型を決める。 // 呼び出せるのは、あくまで同じ戻り値、同じ引数のメソッドのため型を決めておく。 // データ型の名前となる。 // delegate の宣言には、public や private などのキーワードも付けることもできる。 delegate int Sample( int x, int y ); class Class2 { // Signature が同じであることが POINT public int method( int x, int y ) { return x*y; } } class Class1 { static void Main(string[] args) { Class2 instance = new Class2(); // 代理人を作成する際に, Method をもらう. ( Isntance つき. ) // delegate を作成して instance のメソッドを渡す。 Sample sample = new Sample( instance.method ); // 代理人経由で呼び出す. int result = sample( 2, 3 ); Console.WriteLine( result ); } } } POINT delegate を使って異なる処理が一元化できる。
 using System;

 namespace ConsoleApplication21
 {
          
   delegate int Sample( int x, int y );

   class Class2
   {
     public int methodMult( int x, int y )
     {
       return x*y;
     }
     public int methodPlus( int x, int y )
     {
       return x+y;
     }
   }

   class Class1
   {
      // delegate 型を外からもらうことが可能. 
      // 計算方法を外へ出すということ。
     public static void calc( int x, int y, Sample calcMethod )
     {
 //  calcメソッドで、さまざまな計算を実行させたい
//   このdelegateのインスタンスの移譲先として指定するメソッドを入れ替えれば
  計算内容も変わる

       int result = calcMethod( x, y );
       Console.WriteLine( result );
     }

     static void Main(string[] args)
     {
       Class2 instance = new Class2();
       calc( 2, 3, new Sample( instance.methodMult ) );
       calc( 2, 3, new Sample( instance.methodPlus ) );
     }
   }
 }

___

■ Partial


  SYNTAX
    partial class 型名 { ... }

  DESC
    ひとつの長い Class を分割して記述できる. ( コンパイラがあとで結合してくれる. )
    Partial types は、一つのクラス、構造体、インタフェースを複数のファイルなどに分割して記述することができる機能。
    Form の GUIHandler に対して有効に使われる。
    Form のハンドラーで自動生成されるコードは Partial により別ファイルにできる。
    これはユーザ自身のコードと混ざって間違って編集しないようにするため。


          public partial class Form2 : Form
          {
              public Form2()
              {
                  InitializeComponent();
              }

              private void button1_Click(object sender, EventArgs e)
              {
                  // 閉じる. 
                  Close();
              }
          }        
POINT 使いどころは GUI プログラムにおいて、Form を継承したメインウィンドウを担当するクラスを作成するとき。 CallbackMethod が大量に必要になるため. 極めてコード量が多くて管理が難しかったクラスなどは、partial によって複数のクラスに分割するなどして 管理しやすくなる。
     partial class Test {
       static void Main() {
         System.Console.WriteLine(new Test());
       }
     }
     partial class Test {
       public override string  ToString() {
         return "Test";
       }
     }
___

■ Interface




  POINT
    インターフェイスは単なるプログラム言語の機能というだけでなく、
    システム側に用意されたさまざまな便利な機能にアクセスするための入り口という意味合いも持つ。

    いろいろなインターフェイスを自作クラスに実装することにより、システムが用意したさまざまな機能を利用したり、
    自由自在にコントロールしたりできる。
    そういう意味で、インターフェイスを自分で宣言するよりも、
    まずは既存のインターフェイスの活用から始めるとよいだろう。


___

■ インターフェイスの効能

POINT Interface と Inheritance の違いは, Interface は複数可能だが, Inheritance はひとつのみ. その制約が生まれた背景は, MultiInheritance が Trouble をひきおこしやすいため. ので Inheritance はひとつ.Interface は複数という Rule にした. 逆に C++ ではこの Guideline にあわせよう. ^ ^/ 使いどころとしては, Inheritance : 機能性を継承. Interface : クラスの種類に関係なく呼び出し方法を合わせる.  初心者の場合、自分でインターフェイスを定義する機会は多くないかも。 しかし、システム側で定義しているいくつかのインターフェイスを自作クラスに実装すると、 いろいろと便利な機能が有効になる [ システムが定義するインターフェイス ]を活用すると何ができるのかを紹介しよう。 -> 自分が System になって Interface を定義することも可能. ^ ^/ インターフェイスの最も基本的な効能 継承関係がまったくないクラスであるClass2とClass3を定義している。 この2つのクラスには、同じtaskという名前のメソッドがある。 ある外部のメソッドから、Class2とClass3の違いに関係なく、taskメソッドを呼び出したいとしよう。 // インターフェイスを用いて解決した例 using System; namespace ConsoleApplication93 { // Interface を作成. ( Access ないのね. ^ ^/ ) interface ISpecialTask { void task(); } class Class3 : ISpecialTask { public void task() { Console.WriteLine("task() in Class3"); } } class Class1 { static void callTask( ISpecialTask ist ) { ist.task(); } static void Main(string[] args) { Class2 c2 = new Class2(); Class3 c3 = new Class3(); callTask( c2 ); callTask( c3 ); } } } インターフェイスの最も基本的な効能を示すサンプル・プログラム1 異なるクラスにある同じ名前のメソッドを、クラスの違いとは無関係に呼び出す。 通常は継承関係もないクラス間で、共通のメソッドを持つことはできない。 無関係なクラス間で共通のメソッドを持つには、インターフェイスを共有する、 という方法がある。 Class2とClass3は、共通のインターフェイス ISpecialTask を実装している。 ISpecialTaskはtask()というメソッドを実装すべきことを示す。 ここではメソッドの内容は記述されない。あくまで、引数と戻り値だけが指定される。 それぞれのクラスが、このインターフェイスを実装することを明示的に指定している。 callTaskメソッドは、引数としてISpecialTaskインターフェイスへの参照を取るように記述されている。 ISpecialTaskを実装したクラスのインスタンスからは、必ずこのインターフェイスへの参照を取得できる。 つまり、Class2のインスタンスからも、Class3のインスタンスからも取得できることになる。 そのため、33〜34行目のように、どちらのクラスのインスタンスも、引数に渡すことができる (厳密にいえば、インスタンスへの参照から、インターフェイスへの参照が取り出されて それがメソッドに渡る)。  上の例は、インターフェイスを使わなくても、継承を使って実現できる。 using System; namespace ConsoleApplication94 { // 抽象クラスをつくる. abstract class ClassSpecialTask { public abstract void task(); } class Class2 : ClassSpecialTask { public override void task() { Console.WriteLine("task() in Class2"); } } class Class3 : ClassSpecialTask { public override void task() { Console.WriteLine("task() in Class3"); } } class Class1 { static void callTask( ClassSpecialTask ist ) { ist.task(); } static void Main(string[] args) { Class2 c2 = new Class2(); Class3 c3 = new Class3(); callTask( c2 ); callTask( c3 ); } } } POINT インターフェイスを1つだけ実装する場合は 継承を用いても、ほぼ同等の機能を実現できる。 しかし 実装するインターフェイスが2つになると、継承で類似機能を記述することはできなくなる。 以下は、クラスが2個のインターフェイスを実装している例である。 using System; namespace ConsoleApplication95 { interface ISpecialTask1 { void task1(); } interface ISpecialTask2 { void task2(); } class Class2 : ISpecialTask1, ISpecialTask2 { public void task1() { Console.WriteLine("task1() in Class2"); } public void task2() { Console.WriteLine("task2() in Class2"); } } class Class3 : ISpecialTask1, ISpecialTask2 { public void task1() { Console.WriteLine("task1() in Class3"); } public void task2() { Console.WriteLine("task2() in Class3"); } } class Class1 { static void callTask1( ISpecialTask1 ist ) { ist.task1(); } static void callTask2( ISpecialTask2 ist ) { ist.task2(); } static void Main(string[] args) { Class2 c2 = new Class2(); Class3 c3 = new Class3(); callTask1( c2 ); callTask1( c3 ); callTask2( c2 ); callTask2( c3 ); } } } 継承では類似の機能を記述することはできない。 2つのクラスにある2つのメソッドが、それぞれ順に呼び出される。  インターフェイスも継承も、ソースコード上では同じように見える。 クラス宣言のクラス名の後に、コロン記号(:)を書いて、 その後にインターフェイスか継承するクラス名を記述する。 だが、両者の間で決定的に違うのは、インターフェイスの名前は何個でも記述できるのに対して、 継承するクラス名は1個しか記述できないこと。  このような制限は、技術的なものではない。 事実C++では、継承するクラス名をいくつ書いてもよい。 これを多重継承という。多重継承ができれば、インターフェイスはなくてもよい。 事実、C++ にインターフェイスはない。 にもかかわらず、C#やJavaといった比較的新しい言語で多重継承が禁止されているのは、 継承が過剰に強力すぎる機能であり、トラブルを引き起こしやすいという経験による。 強力すぎる継承の機能を、適正な水準まで制限した結果が、継承するクラスは1個までという制約と、 インターフェイスの導入である。 POINT 機能性を継承するときは継承を使う。 呼び出し方法を合わせるときはインターフェイスを使う
___

■ インターフェイスの継承

POINT Interface は 継承できる. ie. Grouping できるので, 基本的な機能は IBase にまとめると便利 ! ^ ^/ Interface は Property, Indexer にも利用可能. インターフェイスはクラスのように継承できる。 using System; namespace ConsoleApplication96 { // Interface // IBaseというインターフェイスを定義する。 interface IBase { void task1(); } // IDerivedというインターフェイスを、IBaseを継承して定義する。 // IDerivedには、IBaseで定義されたメソッドと、 // IDerived自身で定義されたメソッドが含まれるのである。 interface IDerived : IBase { void task2(); } class Class1 : IDerived { // ふたつ実装してね. ^ ^/ public void task1() { Console.WriteLine("task1() called"); } public void task2() { Console.WriteLine("task2() called"); } static void Main(string[] args) { Class1 c1 = new Class1(); c1.task1(); c1.task2(); } } } インターフェイスを実装しているクラスでは、継承されたメソッドも実装する必要がある。 インターフェイスの継承は、大規模なインターフェイスを定義する場合に便利になる。 基本的なインターフェイスを定義してから、 それらを集めて大きなインターフェイスを定義できるからである。 そのため、IDerivedを実装するClass1では、15〜18行目と19〜22行目で、 IBaseで定義されたメソッドと、IDerivedで定義されたメソッドの両方を実装している。 記述の方法はクラスの継承と同じ インターフェイスを実装しているクラスでは、継承されたメソッドも実装する必要がある。 インターフェイスの継承は、大規模なインターフェイスを定義する場合に便利になる。 IBaseというインターフェイスを定義している。 次に IDerivedというインターフェイスを、IBaseを継承して定義している。 つまり、IDerivedには、IBaseで定義されたメソッドと、IDerived自身で定義されたメソッドが含まれるのである そのため IDerivedを実装するClass1では、 IBaseで定義されたメソッドと、IDerivedで定義されたメソッドの両方を実装している。  この機能は大規模なインターフェイスを定義する場合に便利だといえる。 基本的なインターフェイスを定義してから それらを集めて大きなインターフェイスを定義できるから。 プロパティ、インデクサ、イベントとインターフェイス  メソッドだけでなく、プロパティ、インデクサ、イベントもインターフェイスに記述できる。 using System; namespace ConsoleApplication97 { public delegate void SampleEvent(object sender, EventArgs e); // インターフェイスの定義部分 // 6行目でinterfaceの前にpublicが付いているのは、 // 5行目のdelegateの宣言に付くpublicと水準を合わせるためのもの // 水準を合わせないとエラーになる。 public interface ISample { // 普通はgetやsetの後ろに中括弧を書いて // 処理内容を書くのだが、ここはインターフェイス内なので、実装は書かない。 // ここでgetだけ、あるいはsetだけ書けば、読み出し専用、あるいは書き込み専用のプロパティを強制できる。 int SampleProperty { get; set; } // インデクサ getとsetに関しては、プロパティと同じ string this[int index] { get; set; } // イベントの宣言 // インターフェイス内のイベントは、当然、単なる宣言にすぎないので、実装しなければ使えない。 event SampleEvent sampleEvent; } class Class1 : ISample { private int sample = 0; // プロパティ public int SampleProperty { get { return sample; } set { sample = value; } } private string [] ar = new string[3]; public string this[int index] { get { return ar[index]; } set { ar[index] = value; } } public event SampleEvent sampleEvent; public void handler(object sender, EventArgs e) { ISample isample = (ISample)sender; Console.WriteLine( isample.SampleProperty ); Console.WriteLine( isample[0] ); Console.WriteLine( isample[1] ); Console.WriteLine( isample[2] ); } public void invokeEvent() { // イベントハンドラで、プロパティおよび配列にセットした値を表示している。 sampleEvent( this, EventArgs.Empty ); } static void Main(string[] args) { Class1 c1 = new Class1(); c1.SampleProperty = 123; c1[0] = "ABC"; c1[1] = "DEF"; c1[2] = "GHI"; c1.sampleEvent += new SampleEvent(c1.handler); c1.invokeEvent(); } } }
___

■ 同名のメソッドを持つインターフェイス

 あるクラスで2つのインターフェイスを実装しようとしたが、 偶然にも同じ名前のメソッドが定義されていたらどうなるだろうか。
 {
   interface ISample1
   {
     void task();
   }
        // あ ! 名前かぶっちゃった. ! 
   interface ISample2
   {
     void task();
   }
  
   class Class2 : ISample1
   {
     public void task()
     {
       Console.WriteLine("task() in class2 called");
     }
   }

   class Class3 : ISample1, ISample2
   {
          // どちらを実装するか明示する. 
     void ISample1.task()
     {
       Console.WriteLine("ISample1.task() in class3 called");
     }
     void ISample2.task()
     {
       Console.WriteLine("ISample2.task() in class3 called");
     }
   }
   class Class1
   {
     static void Main(string[] args)
     {
       Class2 c2 = new Class2();

            // 同一名では次のような単純な呼び出しではうまくいかない. 
       c2.task();
       Class3 c3 = new Class3();

       //c3.task();  // 'ConsoleApplication98.Class3' に 'task' の定義がありません。
            
            // cast してから call() すれば OK. 
       ISample1 i1 = (ISample1)c3;
       i1.task();
       ISample2 i2 = (ISample2)c3;
       i2.task();

     }
   }
 }
Class3(20行目)では、メソッドの定義をISample1.task(22行目)や ISample2.task(26行目)のように、実装するインターフェイスを明示して記述する必要がある。 オブジェクトをインターフェイスにキャストしてからメソッドを呼び出す必要がある。 POINT この2つのインターフェイスを 20 行目のように1つのクラスで実装しようとすると、 戻り値も引数も共通なので、そのままでは区別することができない。 そこで、どのインターフェイス由来のメソッドを実装しようとしているのか、 インターフェイス名を明示して記述する。 22〜25行目はtaskではなく、ISample1.task と明示的に書くことで、 インターフェイス ISample1 の task を実装していることを明示する。 インターフェイス ISample2 の task を実装している。 まずインターフェイスにキャストしてから呼び出せば、曖昧さはなくなる。 クラスClass3のインスタンスを、インターフェイスISample1にキャストしている。 キャスト結果に対して40行目ではtaskメソッドを呼び出しているが、このコードは間違いなく、 インターフェイスISample1のtaskメソッドを呼び出す。 自作クラスを foreach 可能にする POINT System が用意した Interface を実装することで, 各種サービスをうけることが可能. POINT システムで定義されたインターフェイスを自作クラスに実装することで便利機能を使うことができる。 まず最初は、自作クラスを foreach 可能にする方法からだ。  C# の foreach 文は、VisualBasic などから取り入れた構文と思われるが、配列などを渡すだけで、 自動的にすべての要素を繰り返してくれる便利なもの。 これは、.NET Frameworkに含まれる多くのクラスで利用できるが、 なぜこれほど多くのクラスで使えるかというと、 繰り返しのメカニズムを、インターフェイスを経由して実現しているからだ。 つまり、インターフェイスさえ実装されていれば、どんなクラスであろうと、 foreach で利用できる。 using System.Collections; { // IEnumerator Interface を実装する. // 要は Interface class ClassMyEnumerator : IEnumerator { private int pointer; string [] target; object IEnumerator.Current { get { return target[pointer]; } } bool IEnumerator.MoveNext() { if( pointer >= target.GetUpperBound(0) ) return false; pointer++; return true; } void IEnumerator.Reset() { pointer = target.GetLowerBound(0)-1; } // Ctor public ClassMyEnumerator( string [] array ) { target = array; pointer = target.GetLowerBound(0)-1; } } // IEnumerable Interface を実装することで, foreach 可能. class ClassSample : IEnumerable { string [] ar = { "ABC", "DEF", "GHI" }; IEnumerator IEnumerable.GetEnumerator() { // return new ClassMyEnumerator(ar); } } class Class1 { static void Main(string[] args) { ClassSample c = new ClassSample(); foreach( string s in c ) { Console.WriteLine(s); } } } } IEnumerableとIEnumeratorの2つのインターフェイスを使用する。 クラス内であらかじめ定義されている文字列を foreach文を使用して順に取り出すことができる。  ここでは2つのインターフェイスが登場している。 IEnumerable で、これを実装したクラスは、foreach 可能と見なされる。 IEnumerator で、実際に要素を1個ずつ列挙する機能を提供する。 IEnumerableは、あなたの自作クラスに実装し、 IEnumeratorはあなたの自作クラスを列挙するクラスに実装するものである。 ここでは、3つの文字列を内部に持つクラスが定義されている。 実際には32行目の配列はそのまま foreach 可能なので いちいちインターフェイスを実装しなくてもよいのだが、 foreach 可能にするには、IEnumerableを実装するのだが、 これには、GetEnumeratorというメソッドだけが宣言されている。 そこで、33〜36行目で、そのメソッドを実装している。 やるべきことは、列挙を実際に行うクラスのインスタンスを作成して、それを返してやるだけだ。  実際に列挙するのは、ClassMyEnumeratorクラス IEnumeratorに含まれる Currentプロパティ、 MoveNextメソッド、 Resetメソッド を実装すればよい。 Resetメソッドは、列挙の初期化をする。 列挙は、最初のアイテムの1個手前の状態から始まるので、22行目の最後でマイナス1をしている。 MoveNext は、次の要素に1個進める機能を持つ。 アクセスする対象を示す値をプラス1するのが基本動作だ。 そして、いよいよデータそのものにアクセスするのが、Currentプロパティだ。 これは読み出し専用で、MoveNextメソッドで進めた場所にある内容を返す。 なお、GetUpperBoundメソッドは配列の上限の添え字を返すメソッド、 GetLowerBoundは同様に下限を返すメソッドである。  以上のような処理が記述されていれば、自作クラスのインスタンスを、 foreach 文で使うことができる。
___

■ インターフェイスの基本

__SYNTAX__ [attributes] [modifiers] interface identifier [:base-list] {interface-body}[;] DESC そもそも 型安全のためにある仕組み. 1. POINT 1. 機能( Signature )は必要だけど, 中身は不要の場合に作成する. 1. interface と abstract は異なる. 1. C++ では, 同一の Signature であっても問題ないらしい. class IA { public: virtual void func( int ) = 0; }; class IB { public: virtual void func( int ) = 0; }; class Concrete : public IA, public IB { public: void func( int a ) { dpi( a ); } }; // Concrete を作成して, funcA, funcB に渡すのは問題なし. // 要は, c が生成できたということは, すべての Method の実装が終了すみであり, // interface をわたすことで問題がないはず. { Concrete c; funcA( c ); funcB( c ); } 多重継承ではなくインターフェイスという技術 抽象的にクラスの動作「ビヘイビア」を宣言します. ( 要はこんな機能が必要だよねを宣言. ) クラスは1つの基底クラスしかもつことができないが、インターフェイスはいくつも実装できる。 物理的な動作を見ると「実装する」という表現が適確なので この場では「実装する」という表現を使う。 POINT インターフェイスの役割は、クラスに対する契約。 ビヘイビアを必ず実装しなければならない。 プロジェクトの設計者がインターフェイスを設計して その仕様をアプリケーション設計者に指示すれば 異なるコンポーネント間でも、インターフェイスを通じて同じ操作を保証できる。 Standard 型は、実装はともかく WriteName() というシグネチャを保証しているのです Standard を実装するクラスは Standard 形であるとも考えられ このプログラムのように異なるクラスを同一の型として扱うことができる。 POINT インターフェイスは常に公開される必要があるため メンバはアクセス可能性を指定されない。 インターフェイスの結合 インターフェイスは、他のインターフェイスを含むことで結合できる public interface IBreath { int BreathCount { get ; } void Touch() ; } // もう一例.   public interface IPerson   {   string getName(); // access 修飾子は不要らしい.   }
___

■ Enum(列挙型)


  POINT
    列挙型とは数値( 整数 )に名前を割り当てたもの. 
    enum は C, C++ と同じ. 
    数値に別名をつけたもの. ( define の連続番. )

  POINT
   プログラムを作成していると、
    文字列なのだが出現する可能性のある綴りが限定されている
    というデータに出会うことがある

  例えば
    日本の年号は「明治」「大正」「昭和」「平成」と続いているが、

  これは通常の文字列とは性格が異なる。
  文字列なら「太郎」や「花子」を代入してもよいが、それらは年号ではない。
  かといって
  「明治」「大正」「昭和」「平成」を0から3までの整数に割り当ててコーディングしたら
  意味不明になる。
  ある年が「大正」かどうかを判定するために、1かどうか判定する式を書くことになるが、
  この1が「大正」であることは容易には読みとれない。

  そこで、数値に名前を割り当ててコーディングしたりするわけだが、
  C#では、このような目的に適する列挙型(enum)というデータ型が用意されている。




 using System;

 {

      // C, C++ と同じ
      // Era という名前で、Meiji、Taisho、Showa、Heiseiという4つの名前を含む列挙型を定義する。
      //  宣言された列挙型は、文字列というよりも数値のように振る舞うと考えておけば間違いない。
   enum Era
   {
     Meiji,
     Taisho,
     Showa,
     Heisei, // 最後に [,] をいれても良い。
   }

   class Class1
   {
     static void WriteEra( Era t )
     {
       switch( t )
       {
         case Era.Meiji:
           Console.WriteLine("明治");
           break;
         case Era.Taisho:
           Console.WriteLine("大正");
           break;
         case Era.Showa:
           Console.WriteLine("昭和");
           break;
         case Era.Heisei:
           Console.WriteLine("平成");
           break;
       }
     }
     static void Main(string[] args)
     {
       Era t = Era.Taisho;
       WriteEra( t );
     }
   }




  列挙型の値“Era.Taisho”に対応した年号を文字列で表示している。 



  列挙型を Console.WriteLine する

 using System;

 namespace ConsoleApplication2
 {
   enum Era
   {
     Meiji,
     Taisho,
     Showa,
     Heisei
   }
   class Class1
   {
     static void Main(string[] args)
     {
       Era t = Era.Taisho;
       Console.WriteLine( t );
     }
   }
 }

  列挙型の値の名前そのものが出力された。 


  列挙型をConsole.WriteLineメソッドに直接渡すと、その名前そのものが出力される。
  C++プログラマは、この点で挙動が異なることに注意していただきたい。
  C++では、名前ではなく数値が出力される。

  C++の列挙型は数値の別名にすぎないが、
  C#の列挙型はもう少し多くの管理情報を実行時に持っていて、機能的に同じではない。
  列挙型で漢字を使う


 using System;

 {

//  C#はキーワードに漢字を使用できる。
   enum Era
   {
     明治,
     大正,
     昭和,
     平成
   }
   class Class1
   {
     static void Main(string[] args)
     {
       Era t = Era.大正;
       Console.WriteLine( t );
     }
   }
 }

 {
   enum Era
   {
     Meiji,
     Taisho,
     Showa,
     Heisei,
   }
   class Class1
   {
     static void Main(string[] args)
     {
       Era t = Era.Taisho;
       Console.WriteLine( t );
     }
   }
 }


___

■ 数値として使う列挙型

POINT   列挙型の中身は数値である。 個々の要素に対応する数値を確かめるには以下のように記述する。
 {
   enum Era
   {
     Meiji,
     Taisho,
     Showa,
     Heisei
   }
   class Class1
   {
     static void Main(string[] args)
     {
       Console.WriteLine( (int)Era.Meiji );
       Console.WriteLine( (int)Era.Taisho );
       Console.WriteLine( (int)Era.Showa );
       Console.WriteLine( (int)Era.Heisei );
     }
   }
 }
列挙型の値を数値にキャストして表示する。 最初の項目が0に割り当てられ、それ以降1ずつ値が増えているのが分かる。 列挙型の値は数値にキャストできる。 何も指定しない場合 列挙型はint型の数値で内部処理するので int型へ問題なくキャストできる。 最初の項目が0に割り当てられ、それ以降1ずつ値が増えていく。
___

■ 内部処理用の数値型の変更

 何も指定しなければ、列挙型はint型で内容を処理する。 しかし、内容の処理に使われるデータ型は、必要なら指定して変更することもできる。 メモリの節約のためにより少ないビット数の数値型にすることもできるし、 表現力を高めるためにより多くのビット数からなる数値型にもできる 符号付きと符号なしも選べる。 { enum Era : byte { Meiji, Taisho, Showa, Heisei } class Class1 { static void Main(string[] args) { Console.WriteLine( (byte)Era.Meiji ); Console.WriteLine( (byte)Era.Taisho ); Console.WriteLine( (byte)Era.Showa ); Console.WriteLine( (byte)Era.Heisei ); } } } 列挙型の名前を書いた直後に使用したい数値型を記述する。 内部処理で使用される型をbyte型に指定している。 列挙型の名前を書いた直後に、コロン記号を置いて その後に使用したい数値型を書く。 これにより、Era型はbyte型の整数で内部処理される。
___

■ privateな列挙型

 列挙型の宣言には クラスと同じように private などのキーワードを付けて利用範囲を制限できる。
 {
   class Class2
   {
        // Access 権をつけることも可能.
     private enum Era
     {
       Meiji,
       Taisho
     }

     public static void WriteMeiji()
     {
       Console.WriteLine( Class2.Era.Meiji );
     }
   }
   class Class1
   {
     enum Era
     {
       Showa,
       Heisei
     }
     static void Main(string[] args)
     {
       //Console.WriteLine( Class2.Era.Meiji );  //エラー 'ConsoleApplication7.Class2.Era' はアクセスできない保護レベルになっています。
         Class2.WriteMeiji();
         Console.WriteLine( Class1.Era.Showa );
     }
   }
privateを付けた列挙型は、それを宣言したクラス内からのみ使用できる。  ここでポイントになるのは、2つのEra型が定義されていることだ。 Era型はClass2クラスの中で宣言され、 privateキーワードが付いているので、クラス外からは利用できない。 しかし同じクラス内からは問題なく利用することができる。 「エラー 'ConsoleApplication7.Class2.Era' はアクセスできない保護レベルになっています。」というエラーになり、コンパイルできない。
___

■ 名前に任意の数値を与える

列挙型の個々の要素には 何も指定しなければ、0から順番に1ずつ大きくなる値が割り当てられる。 しかし必要なら、値を明示的に指定できる。
 {

      // 任意の値を割り当てることも可能.
   enum Sample
   {
     A,       // 0
     B,       // 1
     C = 10,
     D,       // 11
     E = B    // 代入も可能. 
   }

   class Class1
   {
     static void Main(string[] args)
     {
       Console.WriteLine( (int)Sample.A );
       Console.WriteLine( (int)Sample.B );
       Console.WriteLine( (int)Sample.C );
       Console.WriteLine( (int)Sample.D );
       Console.WriteLine( (int)Sample.E );
     }
   }
列挙型である Sample 型の要素CおよびEに値を割り当てている。 サンプル・プログラム8の実行結果 “10”を割り当てた“C”の次の要素“D”は“11”となっている。 何も指定していない7〜8行目のAとBは0と1になる。 しかし、Cは、明示的に“C = 10”としているので、 10という値を持つ Dは、 Cの次なので11になる。 E は、“E = B”として、Bと同じとしているため、Bの値、つまり1になる。  列挙型の値を変更するのは、数値に何か特別な意味があって、それと一致させたい場合である。 逆に数値に特別な意味がないのに、数値を明示的に指定すると予期せぬトラブルが起こる場合があるので、 必要性を見極めてから利用すること。
___

■ 数値指定時のトラブル例

POINT Enmu はクラスであり, static string GetName() により名前を取得可能 static GetNames() によりすべての 文字を取得できる.  以下は、数値指定時に起こるトラブルの一例である。 内容としては、ただ単に列挙型の値をコンソールに出力しているだけで何も問題がないように見える。
 {
      // クラスの外に指定した場合は, public 属性らしい. 
   enum Sample
   {
     A,
     B,
     C = 10,
     D,
     E = B
   }


   class Class1
   {
     static void Main(string[] args)
     {
       Console.WriteLine( Sample.A );
       Console.WriteLine( Sample.B );
       Console.WriteLine( Sample.C );
       Console.WriteLine( Sample.D );
       Console.WriteLine( Sample.E );
     }
   }
“B”が出力されると予想される場所に“E”が表示されている。 POINT Enum は内部では数値として格納される. 数値の割り当ては初心者は避けた方がいい. [1] < -> [B|E]  見て分かるとおり、本来 A〜E が順に出力されるはずが、Bの表示がEに化けている。 その原因は、“E = B”にある。列挙型が内部的に数値で処理されるため、 同じ数値に割り当てられた名前は区別できない。 つまり、Sample.Bを使ったのか、Sample.Eを使ったのかは 列挙型の値からは区別できない。 そこで、Sample.Bを指定しているのに、 同じ値を持つ E がコンソールに表示されてしまうという現象が起きるのである。 これを回避するには、[ 同じ値 < -> を別の名前 ]に割り当てなければよい。 数値を明示的に指定しなければ、自動的に同じ値の割り当ては回避されるので、 初心者は数値指定を避けた方がトラブルに巻き込まれる危険が減る。 POINT Enum は内部的に数値として処理される. そのため, 限定的に数値演算が可能.
___

■ 列挙型の数値計算

 列挙型は数値ではないが、内部的に数値として処理される。 そのためすべてではないが、限定的な数値計算ができる。
 {
      // Enum 
   enum Sample
   {
     A,
     B,
     C
   }
   class Class1
   {
     static void Main(string[] args)
     {
       Sample a = Sample.A;
          // 列挙型に 1 を足すのはあり, ( 意味としては次にいく. ^ ^/ )
       Sample r = a + 1;
       Sample b = Sample.B;

          // Enum 同市の 演算はできない. 
       //Sample s = a + b; //演算子 '+' を 'Console10.Sample' と 'Console10.Sample' 型のオペランドに適用することはできません。
       Console.WriteLine( r );

     }
   }
 }
“A”に1を足したため、次の要素を示す“B”が表示されている。 Aが代入されている変数aに1を足すコードが記述されている。 このコードは正常に実行でき、結果はBとなる これは、5〜10行目の列挙型の定義で、Aの次がBであることによる結果である。 一方、 列挙型の値と列挙型の値を足し算するような計算はエラー扱いとなり コンパイルできない。 Aに1を足すと次の要素を示す、という計算に意味はあっても、 AとBを足す、という計算に意味はないからだ。
___

■ 値の名前を得る

POINT C# の列挙型は System.Enum を継承したものとして, 自動で定義される. -> ie. Enum の Method を利用できる. ^ ^/  C#の列挙型は、すべて System.Enum クラスを継承したものとして自動的に定義される。 そのため、System.Enum クラスを処理するためのさまざまなメソッドがすべて利用できる。 例えば、列挙型のある値に対応する名前を得るには、以下のように記述できる。
 {
      // Enum もクラスらしい. 
   enum Era
   {
     Meiji,
     Taisho,
     Showa,
     Heisei
   }
   class Class1
   {
     static void Main(string[] args)
     {
       Era t = Era.Taisho;

          // 列挙型に対応する名前を取得する. ( Enum 型と値を指定する. )
       string name = Era.GetName( Type.GetType("ConsoleApplication11.Era"), t );
       Console.WriteLine( name );a
     }
   }
System.Enum クラスにある GetName メソッドを使用している。 GetName メソッドに列挙型の値を渡すと、それに対応している名前が文字列で返される。 GetName メソッドは、 System.Enum クラスの static なメソッドである。 第1引数に列挙型の型を、第2引数に判定したい値を渡すと、列挙型の名前を返してくれる。 つまり、Era.Taisho という値を GetName メソッドの第2引数に渡すと、 対応する文字列“Taisho”が返される。 列挙型のすべての名前を得る
 {
   enum Era
   {
     Meiji,
     Taisho,
     Showa,
     Heisei
   }
   class Class1
   {
     static void Main(string[] args)
     {
          // すべてのの名前を取得することも可能.
       string [] names = Era.GetNames( Type.GetType("ConsoleApplication12.Era") );
       foreach( string s in names )
       {
         Console.WriteLine( s );
       }
     }
   }
 }
System.Enum クラスにある GetNames メソッドを使用している。 列挙型である Era 型に含まれるすべての要素の名前が列挙される。 GetNames メソッドは、引数として与えられた列挙型に含まれるすべての名前を、 文字列の配列として返す。 これで列挙型に含まれるすべての名前がわかる。
___

■ 列挙型のすべての数値を得る

名前ではなく、数値の一覧を取得する方法
 {
   enum Sample
   {
     A=1,
     B=20,
     C=300,
     D=4000
   }
   class Class1
   {
     static void Main(string[] args)
     {
          // GetValues() Method を利用する
       System.Array values = Sample.GetValues( Type.GetType("ConsoleApplication14.Sample") );

       foreach( int n in values )
       {
         Console.WriteLine( n );
       }
     }
   }
System.Enumクラスにある GetValues メソッドを使用している。 列挙型に含まれる数値の一覧が表示される。  GetValues メソッドは、ある列挙型に含まれる数値の一覧表を配列として返す機能を持つ。 このメソッドに与える引数は GetNames メソッドのそれと同じ。 戻り値は System.Array 型だが、これはすべての配列のスーパー・クラスに当たるもので、 どのような配列を渡すのにも使用できるデータ型である。 列挙型はどんな数値型で表現するかをプログラマが指定できるので、 汎用的なデータ型で戻ってくる。 文字列から列挙型の値へ  外部との入出力に列挙型の名前を使う場合、 入力された文字列を列挙型の値に変換しなければならない。 そのために System.Enum クラスには、Parse メソッドが存在する。 このメソッドは、“Taisho”などの文字列を入力して、その文字列を解析し、 それが指定列挙型の Taisho であることを突き止めて、その値を返す。
 {

   enum Era
   {
     Meiji,
     Taisho,
     Showa,
     Heisei
   }
   class Class1
   {
     static void Main(string[] args)
     {
       string target = "Taisho";
          // 文字列から 数値へ
       Era x = (Era)Enum.Parse( Type.GetType("ConsoleApplication15.Era"), target );
       Console.WriteLine( x );
       Console.WriteLine( x.GetType().FullName );
     }
   }
System.EnumクラスのParseメソッドを使用している。 得られた列挙型の値と、その列挙型のフルネームを表示する。 このメソッドの最初の引数は、列挙型の型である。 2番目の引数には、変換する文字列を指定する。 このメソッドから戻ってくる値は、汎用的なobject型なので “(Era)”のようにキャストする必要がある。 文字列への変換  列挙型の値から対応する名前を得る方法はすでに説明したが これとは少し違う方法もある。
 {
   enum Sample
   {
     Alpha,
     Bravo,
     Charlie=10,
     Delta=11
   }
   class Class1
   {
     static void Main(string[] args)
     {
       Sample sample = Sample.Delta;
       string name1 = sample.ToString();
       Console.WriteLine( name1 );
       string name2 = sample.ToString("G");
       Console.WriteLine( name2 );
       string name3 = sample.ToString("X");
       Console.WriteLine( name3 );
       string name4 = sample.ToString("D");
       Console.WriteLine( name4 );
     }
   }
ToStringメソッドでは、フォーマット文字列を指定することによって 値を16進数などの文字列でも表示できる。  列挙型の値にはToStringメソッドを利用できる。 ToStringメソッドはすべてのクラスが持つメソッドだが、 列挙型では、値に対応する名前を返すように実装されている。 さらに引数にフォーマット文字列を指定して、 複数のフォーマットで文字列に変換できる。 “G”を指定した場合は名前を返す。 “X”の場合は16進数の文字列として返す。 “D”の場合は10進数の文字列として返す。  このほか、ToStringメソッドにはフォーマット機能を持つインターフェイスを指定して 変換する機能などもある。 まとめ 列挙型 は、決まりきったいくつかの名前を扱うときに便利な機能である。 C++などに見られる単なる数値の別名にすぎない列挙型よりも強力であり、 これらの言語で文字列として処理していたものが、C#では列挙型で処理すべきものになるかもしれない。
___

■ コンストラクタとデストラクタ


___

■ コンストラクタ

using System; namespace ConsoleApplication36 { class Class2 { public Class2() { Console.WriteLine("public Class2()"); } } class Class3 { public Class3( int x, int y ) { Console.WriteLine("public Class3({0},{1})",x,y); } } class Class1 { static void Main(string[] args) { Class2 t1 = new Class2(); Class3 t2 = new Class3( 1, 2 ); } } } Class2クラスのインスタンスが POINT 引数の異なる同名のメソッドを扱う機能がある これをオーバーロードという. new で呼び出す Ctor は Signature で決まり ひとつのみ. -> C# では複数呼び出しが可能. 明示的には呼び出されていないコンストラクタが呼び出されている baseクラスのコンストラクタの呼び出し POINT bCls から生成される. そのとき コンストラクタの引数は 継承先クラスのコンストラクタへ渡されるため、 そのままでは継承元クラスのコンストラクタへ引数は渡せない。 継承元クラスに、引数の無いコンストラクタが定義されていればそれを呼び出す。 引数の無いコンストラクタが定義されていないクラスなら、非常に困ったことになる。  この問題を解決するためにC#には、:thisと似ている:baseという構文が用意されている。 using System; namespace ConsoleApplication27 { class Class1 { static void Main(string[] args) { ClassDelived cd = new ClassDelived( 10, 20 ); Console.WriteLine(cd.x1); Console.WriteLine(cd.y1); Console.WriteLine(cd.x2); Console.WriteLine(cd.y2); } } }  この流れを数値の変化として見るなら、 27行目でnewの引数として指定されている数値は10と20である。 もちろん、これは、17〜21行目のコンストラクタには10と20として伝達される。 しかし、“: base( x+1, y+1 )”という記述により、 コンストラクタに伝達される引数は11と21になる。 まず+1された引数とともに、継承元クラスのコンストラクタが実行される。 VarMem の初期化は宣言時に代入してしまう. EX: public int x = 1; Ctor より先に処理される. using System; namespace ConsoleApplication28 { class Test1 { public int x = 1; public Test1() { Console.WriteLine( x ); } } class Test2 { public int x; public Test2() { Console.WriteLine( x ); x = 2; Console.WriteLine( x ); } } class Test3 { public int x = 1; public Test3() { Console.WriteLine( x ); x = 2; Console.WriteLine( x ); } } class Class1 { static void Main(string[] args) { Test1 t1 = new Test1(); Test2 t2 = new Test2(); Test3 t3 = new Test3(); } } } クラス内のメンバ変数に直接初期値を指定したサンプル・プログラム5 その疑問に答えるために、もう1つのクラスを用意した。 メンバ変数の初期化とコンストラクタ内での代入の双方を行った場合の例  ここまでのサンプルでは、すべてコンストラクタに public キーワードを付けてきた。 もし、これにprivateキーワードを付けると何が起こるのだろうか。 言い換えれば  これにより、インスタンスが生成できないクラスが出来てしまうわけだが こんなクラスにも立派な用途がある using System; namespace ConsoleApplication31 { public static void special( string message ) { Console.WriteLine( message ); } } class Class1 { static void Main(string[] args) { // static Method を利用する. Class2.special("Hello!"); // Class2 t = new Class2(); // エラーになる 'ConsoleApplication31.Class2.Class2()' はアクセスできない保護レベルになっています。 } } }  ここで注意すべきことは、 staticなメソッド呼び出しは問題なく実行可能であるが、 newは実行できないことである コンパイル時に「.Class2.Class2()' はアクセスできない保護レベルになっています。」というエラーが発生する。 コンストラクタにprivateキーワードを付けることによって、 そのクラスからのインスタンス生成を禁止できる。
 {
   class Class1
   {
     static void Main(string[] args)
     {
          // Ctor をよんで, special ( ちょっとわかりずらい. )
       Class2.special();
     }
   }
 } 
___

■ デストラクタ

 {
   class Class2
   {
        // Dtor ( 明示的に呼ばないので, Access 修飾なし  ) 
     ~Class2()
     {
       Console.WriteLine("Class2's destructor called");
     }
   }
   class Class1
   {
     static private void test()
     {
       Console.WriteLine("static private void test() called");
       Class2 t1 = new Class2();
       Console.WriteLine("static private void test() done");
     }
     static void Main(string[] args)
     {
       Console.WriteLine("static void Main(string[] args) called");
       test();
       Console.WriteLine("static void Main(string[] args) done");
     }
   }
 } 
このサンプル・プログラムの場合には Main メソッドの実行後に呼び出されているのが分かる。 Class2クラスのインスタンスは15〜19行目のブロックの内部で宣言された変数t1を経由してしか参照できないので、 メソッドtestの実行が終わればインスタンスは用済みに思える。 これがC++のようなプログラム言語なら ここでデストラクタを実行させることができるのだが、C#では動作が異なっている。 デストラクタが実行されるのは、メソッドtestの実行が終わり さらにメソッドMainの実行が終わった後ということになっている もちろん、必ずこのタイミングでデストラクタが実行される保証はない
___

■ 確実な終了処理

例外処理を用いて上のサンプルソースを書き直してみたものである。
 {
   class Class2
   {
     public void close()
     {
       Console.WriteLine("public void close() called");
     }
   }
   class Class1
   {
     {
       Console.WriteLine("static void Main(string[] args) called");
       test();
       Console.WriteLine("static void Main(string[] args) done");
     }
   }
 } 
returnでメソッドから直接抜けようが他の例外が発生しようが、どんな理由でも、 tryブロックの実行が終了するときにfinallyブロックは実行される。 20行目の処理がどんな結果に終わろうとも(プロセスそのものが異常終了でもしない限り)必ず24行目は実行される。
___

■ destructor


  POINT
   コンストラクタはインスタンスの内容を正しい状態からスタートさせるために重要な機能.
    一方デストラクタの役割は
    C++など他のプログラム言語に比べて、重要度は下がっている。

    デストラクタが担ってきた役割を
    もっと別のもので交代させていくべきと言えるかもしれない。

   デストラクタとは、コンストラクタの逆の役割を持った機能である。
    インスタンスが消滅する際に呼び出されるメソッドである。

      C# では System が Instance の寿命を管理する.
      ので, Destructor が走る Timing は Control できない.

  WARNING 
    C++ なら、デストラクタに開いたファイルを閉じる処理を記述することも可能だが、
    C#で同じように記述すると、いつファイルを閉じるか予測不可能と言うことになり、
    トラブルの元になりかねない。

    デストラクタは、いつ実行されるか分からない。
    ある範囲の実行を終えた時点で確実に何かの終了処理を実行させたい場合は、例外処理を使うのが普通である。

     static private void test()
     {
       Console.WriteLine("static private void test() called");
       Class2 t1 = new Class2();
       try
       {
         // 何かの処理がここにあると想像してください
       }
       finally
       {
         t1.close();
       }
       Console.WriteLine("static private void test() done");
     }
     static void Main(string[] args)


POINT IDisposable と using を利用すると, 確実な終了処理を記述できる. IDisposable と using による終了処理 IDisposable というインターフェースと、using というステートメントを使うと、 確実な終了処理を記述できる。 相変わらずデストラクタはずっと後になって実行されている。 つまり、Dispose メソッドが呼び出されたからと言って、 インスタンスが消滅したわけではないのである。 Disposeメソッドは後処理を行うチャンスをくれるもの
   class Class2: IDisposable
   {
     ~Class2()
     {
       Console.WriteLine("Class2's destructor called");
     }

        // 確実に呼び出す終了処理
     void IDisposable.Dispose()
     {
       Console.WriteLine("void IDisposable.Dispose() called");
     }

   }

   class Class1
   {
     static private void test()
     {
       Console.WriteLine("static private void test() called");

          // この使い方で, IDisposable の Dispose() を呼ぶらしい.
          確実な終了処理をさせたいクラスで、IDisposable インターフェイスを実装する
  これには、Dispose メソッドしかないので、
  そして、usingステートメントを使って、インスタンスを生成する

       using(Class2 t1 = new Class2())
       {
         // ここに何かの処理があると想像してください
       }

       Console.WriteLine("static private void test() done");
     }
     static void Main(string[] args)
     {
       Console.WriteLine("static void Main(string[] args) called");
       test();
       Console.WriteLine("static void Main(string[] args) done");
     }
   }


___

■ ステートメント


___

■ ステートメントとは

 {
   public class Class1
   {
     public static int Main(string[] args)
     {
       int a=1;
       {
         int b=2;
         Console.WriteLine(a);
         Console.WriteLine(b);
       }
       Console.WriteLine(a);
       // Console.WriteLine(b); // エラーになる
       return 0;
     }
   }
 } 
ブロック・ステートメントを使用するサンプル・プログラム  11行目で宣言された変数bは、ブロック・ステートメントの中で宣言されているので 寿命はブロックの範囲内に限定される 超えると変数bは存在しなくなる 変数bを利用しようとするとコンパイル・エラーになる。 ブロックが終わった後で使いたい変数はブロックの外で宣言する必要がある。
___

■ 分岐

 条件によって実行するステートメント( 文 )を変える手段は2つある。 ifステートメントによる条件判断で、条件判断式が成立したときと 成立しないときで処理内容を分ける。 switchステートメントで、式の値によって処理を分ける。 if条件式に書かれたargs.Lengthは コマンドライン引数に何個の値が指定されたかを示すプロパティ。 ifは、“args.Length != 0”つまりコマンドラインに何か書いてあれば11行目を実行し、 “{”と“}”はブロック・ステートメントである。 switchは、カッコ内の値と、続くcaseの後の値を比較し 一致したらcase以後のステートメントを実行する。 つまり、args.Lengthの値がゼロなら、 “case 0”が成立して実行される。 caseは何個でも書くことができるが、 合致しない場合は“default:”が実行される。  switchステートメントの中で使用されるbreakは 処理を終了してswitchステートメントから抜け出す機能を持ったキーワードである。 breakが出現すると、そこで処理は打ち切られて switchステートメントの次のステートメントが実行される。 POINT 処理を終了させるのではなく、別のcaseの条件処理にジャンプさせたい場合がある。 そのような場合は、breakの代わりに“goto case”という構文を使う。 args.Lengthが1のとき、30〜31行目が実行されるが “goto case 2”によって、“case 2”へ制御が移り実行される。 “goto defalt”はdefaultへ制御を移す機能を持っていて やっとswitchステートメントから脱出することになる。  ここで、C/C++プログラマは注意して欲しいのだが POINT: C# switch Statement は String を使うことができる “args[0]”は、コマンドラインの引数に指定された最初の文字列を示す
___

■ Dispose



  
  POINT
    using を利用して Dispose を自動で呼べる。

  DESC
    C#では、オブジェクトが使われなくなったときにするべき処理は
    Dispose() に実装するのが一般的。

    IDisposable インタフェースが Dispose() メソッドを持っています。
    Dispose() を実装するクラスは、IDisposable を継承すること。
    C# では何らかの終了処理が必要な場合は、以下のようにする必要がある。

    class MyClass : IDisposable {
      public MyClass() {
      }
      public void Dispose() {
        // 終了処理
      }
    }

    Dispose() の呼び出し例

    public void AnyMethod() {
      MyClass obj = new MyClass();
      obj.DoSomething1();
      obj.DoSomething2();
      obj.Dispose();
    }
* クラスの設計者は、IDisposableを継承して Dispose() で終了処理を実装 * クラスの使用者は、クラスのオブジェクトを使用しなくなったら Dispose() を[ 明示的 ]に呼ぶ IDisposable を継承せずに Dispose() を実装しても良い? 望ましくありません。 スコープを抜けたときに Dispose() を自動で呼び出してくれる using が機能しなくなります。 using を積極的に使うことで Dispose() 忘れを防ぐことができるので、 IDisposableを継承してDispose()を実装すべき。 C++に馴染んできた人は、 C++のようにデストラクタがスコープ抜けたら自動で呼ばれる方がいいと思う。 ただそれはオブジェクトがローカル変数の場合。 void AnyMethod() { MyClass obj(); obj.DoSomething1(); obj.DoSomething2(); } これは AnyMethod() を抜けたら自動で obj のデストラクタは呼ばれます。 void AnyMethod() { MyClass* obj = new MyClass(); obj->DoSomething1(); obj->DoSomething2(); delete obj; } このように new を使ってオブジェクトを生成したら、 C#のDispose()同様にdeleteを最後に呼ぶ必要があります。 POINT C#の場合、クラスからオブジェクトの生成をする場合は、常にnewを使う。 ただし delete は呼ぶ必要はない。 deleteに相当する処理はGCが適当なタイミングでやってくれるから。 POINT C#で Dispose() を呼ばなければならないケースより、 C++で delete を呼び出さなければならないケースの方が多いはず。 また C# には、usingを使って Dispose() 自動呼び出しもできる。 C++より解放忘れのリスクは少ない。
___

■ キャストとデータ変換(cast)



// 元の値(123)がそのまま各変数に引き継がれる。 
     {
       sbyte b = 123;
       short s = b;
       int i = s;
       long l = i;
       Console.WriteLine("{0},{1},{2},{3}",b,s,i,l);
       return 0;
     }



     {
       long l = 123;
       int i = l;
       short s = i;
       sbyte b = s;
       Console.WriteLine("{0},{1},{2},{3}",b,s,i,l);
       return 0;
     }


  キャストを使用して、自身よりも表現力の小さい変数に値を代入した。コンパイル・エラーも発生しないし、


  POINT: Cast( 型変換 )は Data を壊す場合がある. 

  キャストがデータを壊すとき
 上記の例は、扱う数値がどのデータ型にも収まるほど小さかったので、問題なく処理できた。
  いつも数値が十分に小さいわけではない。
  桁数が多いデータを、それが収まりきらないほど桁数が小さいデータ型の変数に入れてしまう場合もあるだろう。
  キャストを使えばそれは可能である。

  桁数が足りない以上、元の値を保つことが不可能である。

 {
   using System;
   public class Class1
   {
     public static int Main(string[] args)
     {
       long l = 1844674407370955161;
       int i = (int)l;
       short s = (short)i;
       sbyte b = (sbyte)s;
       Console.WriteLine("{0},{1},{2},{3}",b,s,i,l);
       return 0;
     }
   }
 }
正数だったはずが負数扱いされてしまう場合もある。 格納不能な数値をキャストによって強制的に変換して代入したため 数値が破壊されてしまった。 POINT bitiamge では 切り詰める作業をしているだけ. -> しかし整数としてみると,まったく異なる値になる.  10進数ではバラバラの値に見えるが 内部的にはビットイメージの桁数を切りつめる処理が行われているだけ しかし、10進数として見るとまったく予測できない値に変化する。 あくまで10進数の値が重要である場合には、値が大きく狂うような状況が発生しないようにプログラミングしなければならない。 キャストをなるべく使わないように書けば このような問題を自動的に回避できる。 キャストしなければならない必然性が特にない場所でキャストを多用するようなソースコードは 潜在的にバグが入り込む可能性が大きい。 POINT だから、事前に何桁の数値が必要になるかを見積もった上で 使うデータ型を統一し、キャストをできるだけ少なくするようにプログラミングすること。
___

■ 実数でもキャスト

 データ変換は、整数だけでなく、実数でも必要とされる。 C#には、桁数の多いdouble型と桁数の少ない float型という2種類の浮動小数点実数のデータ型がある。 桁数の多いdouble型の値を桁数の少ないfloat型に代入する場合は 整数と同じくキャストが必要。 { double d = 0.123456789012345; float f = (float)d; Console.WriteLine("{0},{1}",d,f); return 0; } POINT double型をfloat型にキャストすると、数値の下の桁が切り捨てられていることが分かる。 前出の整数の場合のように、とんでもない値になってしまうわけではない。 桁数の少ないデータ型に変換されたときに切り捨てられるのは下の桁である。 値のおおまかな値は変化しない。精度が落ちるだけ。 その意味で、整数をキャストする場合に比べれば比較的被害は少ない。 ちなみに、上記画像の最後が“91”で終わっているが、変換元の値を見れば、ここは“89”になるべき場所である。このような食い違いが生じるのは、 2進数の浮動小数点演算が、10進数の表記を正確に表記できないことによって起こる誤差であり、原理的に不可避である。 これを避けるには、2進数ではなく10進数で処理するdecimal型を用いるが、処理速度や効率はfloat型やdouble型の方が勝る。 定数値にキャスト  誤差の出ない計算を行うなら、decimal 型が有利。
 {
   using System;
   public class Class1
   {
     public static int Main(string[] args)
     {
       decimal e = 0.123456789;
       Console.WriteLine("{0}",e);
       return 0;
     }
   }
 }
  このソースコードは、コンパイルを通らない。 d:\w\test\consoleapplication11\class1.cs(8、16): エラー CS0029: 型 'double' を型 'decimal' に暗黙的に変換できません。  これは、何気なく書いた“0.123456789”という値が、実はコンパイラ内部でdouble型の値として認識されていることにより発生したエラー。 つまりコンパイラは、double型の値をdecimal型に代入すると認識して、エラー扱いしたのである。 キャストを用いて“(decimal)0.123456789”と書いてエラーを避けることはできるが、 せっかく誤差を避けるためにdouble型を使わないようにしているのに ここでdouble型が入り込むのは美しくない。
 {
   using System;
   public class Class1
   {
     public static int Main(string[] args)
     {
       decimal e = 0.123456789m;
       Console.WriteLine("{0}",e);
       return 0;
     }
   }
 }
POINT decimal 型定数は m をつけて CMP に通知する. “0.123456789”の後ろに“m”を付けて、“0.123456789m”と記述したことである。 この“m”は、「その値がdecimal型だよ」と言うことをコンパイラに伝える機能を持った文字である。 “u”は符号なし “l”はlong型 “f”はfloat型 “d”はdouble型 といった機能をもった文字を数値の後ろに付けることができる。 無意味なキャストを減らすためにも、これらの文字は有効 定数に型指定文字を追加したら コンパイルも問題なく通過し、実行結果も期待どおりのものになった。
___

■ 符号の有無は要注意

 キャストを用いる際には 符号ありのデータ型と、符号なしのデータ型の違いに注意を払う必要がある。 例えば 同等のビット数で表現されるデータ型の間で変換を行う場合 確かに情報が欠落せずに変換はできる しかし それが指し示す値が同じとは限らない。
 {
   using System;
   public class Class1
   {
     public static int Main(string[] args)
     {
       short i = -1;
       ushort u = (ushort)i;
          // A: -1, 65536
       Console.WriteLine("{0},{1}",i,u);
        
       return 0;
     }
   }
 }
負の数を符号なしのデータ型に代入するプログラム POINT 符号つき変数は( CMP くんの )解釈が異なる. ビットとしての情報は失われていないものの、符号付き変数では値の解釈が異なるため、 POINT: bit Level の情報は失われていない. 変数iと変数uは、機械語レベルで格納されるビットの値としてはまったく同一であり、 ビット単位で処理する演算子を用いるとまったく同じ結果になる。 しかし、[ 数値としての値 ]は同じではないことを、肝に銘じておく必要がある。 言い換えれば、符号付きと符号なしのデータ型は違うものだと認識して、 混用はなるべく避ける方がよい。 POINT 整数型は少数を表現できない 切り捨ては同一符号 && 絶対値が超えない値になる 小数の切り捨て 整数型には小数を表現できない。 キャストによって実数型から整数型に変換させることはできるが、 小数点以下の値はすべて失われる。 { for( float f=-2; f< 2; f += 0.25f ) { int i = (int)f; Console.WriteLine("{0},{1}",f,i); } } POINT 浮動小数点数値を整数型変数にキャストした場合には、 同一符号で絶対値が本来の値(浮動小数点値)を超えない最大の整数値に変換される。 WARNING 四捨五入に慣れたVisual BASICプログラマなどは要注意
___

■ 参照型のキャスト

 クラスからnewによって作成したインスタンスも、キャストの対象になる。 ソース内で宣言したクラスであるClass1のインスタンスをobject型にキャストしている。 object型は、すべてのクラスに対する共通の基底となるクラスなので すべてのクラスのインスタンスは、object型にキャストできる。 { using System; public class Class1 { public String hello; public static int Main(string[] args) { Class1 c = new Class1(); c.hello = "Hello!"; object o = (object)c; Class1 c2 = (Class1)o; Console.WriteLine("{0},{1}",c.hello,c2.hello); return 0; } } } 一度object型にキャストしたものを、 もう1度キャストして元のClass1型に戻している。 もし、object型にキャストされたときに、 インスタンスがobject型に変換されているなら Class1の内部で宣言したhelloという変数はその時点で失われるはず。 そのため、再びClass1型にキャストしても helloに代入した“Hello!”という文字列は失われているはず。 POINT 参照型の Cast は元に戻せば, 元のクラスの Instance として利用可能. キャストによってデータが失われたら永遠に戻らない整数型などの値型と、 クラスなどの参照型の違いである このあたりが C と通ずる。 参照型は、object型などのより基本的な型にキャストしても、 元の型に再キャストすれば、完全に元どおりの機能を取り戻す。
___

■ 無関係の参照型へのキャスト

Class1とは縁もゆかりもない無関係なクラスにキャストしてしまっても、 コンパイラはエラーを通知してくれないと言うこと
 {
   using System;
   public class Class2
   {
     public String hello;
   }
   public class Class1
   {
     public String hello;
     public static int Main(string[] args)
     {
       Class1 c = new Class1();
       c.hello = "Hello!";
       object o = (object)c;
       Class2 c2 = (Class2)o;
       Console.WriteLine("{0},{1}",c.hello,c2.hello);
       return 0;
     }
   }
 }
POINT: Instance の Cast は必要なときのみすること.  ここでは、まったく無関係だが、たまたま同じ hello という変数を持つClass2というクラスを宣言し、 objectクラスから元のクラスにキャストし直す際に、この無関係なClass2を指定してみた。 実際にビルドしてみると分かるが、明らかに間違っているにもかかわらず コンパイラは何もエラーを告げてこない。  しかし、コンパイル・エラーが起こらないといっても、プログラムが動作するはずもない。 実際に実行すると、以下のようなエラーメッセージで強制終了させられてしまう 。型 'System.InvalidCastException' のハンドルされていない例外が D:\w\test\ConsoleApplication11\bin\Debug\ConsoleApplication11.exe で発生しました  このように、コンパイル時に判断できないバグを作り込んでしまう可能性があるので、 インスタンスのキャストは必要なとき以外は使わない方がよい。 一方、どんな型のインスタンスでも扱える便利なクラスを開発する際は、 このリスクを考えに入れても、インスタンスをobject型にキャストして使う価値があると言える。 スーパー・クラスへのキャスト  無関係なクラスへのキャストはエラーになるしかないが、 スーパー・クラスへのキャストは可能。 Class2を継承してClass1が作られているとき、 Class1のインスタンスをClass2にキャストするのは正しい
 {
   using System;
   public class Class2
   {
     public String hello;
   }

      // 継承した.
   public class Class1 : Class2
   {
     //public String hello;
     public static int Main(string[] args)
     {
       Class1 c = new Class1();
       c.hello = "Hello!";
       object o = (object)c;
       Class2 c2 = (Class2)o;
       Console.WriteLine("{0},{1}",c.hello,c2.hello);
       return 0;
     }
   }
 }
POINT SuperClass への Cast は合法  このサンプル・ソースのポイントは、Class1のインスタンスとして作ったはずのインスタンスを objectにキャストしてから 改めてClass2にキャストしていることである Class2はClass1のスーパー・クラスであるため、このキャストは合法である。 helloという変数は、Class2の中だけに存在するものであり 代入したものと、参照している変数helloはすべて同一である。 コメント//を外すと、同じ名前の変数を重複定義しているとコンパイラから怒られる。 スーパー・クラスへのキャストは「合法」なので コンパイルはもちろん、実行時もエラーは起こらない。 今回の例では、Class2にしかない変数helloが2つのインスタンス変数から参照され、 これらが連続して表示されている。 アンボクシングとキャスト  ボクシングしたデータを元のデータ型で取り出すことを「アンボクシング」という。 ボクシングもアンボクシングも、中身は変わらなくても見かけ上のデータ型が変わるので、 キャストとの関係が発生する。 ボクシング/アンボクシングというのは、データ変換の一種。
 {
   public class Class1
   {
     public static int Main(string[] args)
     {
       int i = 123;
       object o = i;
       int j = (int)o;
       Console.WriteLine("{0},{1},{2}",i,o,j);
       return 0;
     }
   }
 }
POINT unboxing には Cast が必要, Boxing は Cast 不要. ボクシングによりobject型に変換した値を、 アンボクシングにより元の型に戻す場合にはキャストが必要となる。 あっさりと整数をobject型に代入しているが、ここでボクシングが発生する。 ボクシングで情報が欠落することはまったくないので、明示的にキャストしなくても問題はない。 これに対して ボクシングした値を整数型変数に代入する時点で、アンボクシングが行われる。 “(int)”とキャストされていることから分かるとおり、明示的なキャストが必要。 ボクシングはキャストはなくてもよいが、アンボクシングはキャストを必須とする と覚えておくこと。 まとめ しかし、C#ならではの特徴もあるので、完全にC/C++/Javaと同一というわけではない。
___

■ ボクシング.boxing


  POINT
    値型を object に wrap して格納する機能のこと。


  まとめ
    また、この2つはC++にもない機能であり、既存のプログラム言語の利用者は戸惑うかもしれない。

    便利な機能を使いこなすには、それらを正しく理解することが欠かせない。

   複数のデータを一括して扱う処理を記述する場合に、
    どのようなデータでも収納できる便利な機能を記述できるとプログラムがすっきりする。
    だが、実用言語で、これを達成したものは多くはない。

    例えばJavaでは、数値型はクラスではないため
    任意のクラスのインスタンスを扱う入れ物となるクラスを設計しても、

    そのままでは数値を格納できない。
    そのため、数値を格納するラッパ・クラス(wrapper class)のインスタンスに一度数値を入れてから
    それを格納する必要がある。

    Visual Basic 6.0のVariant型は何でも格納できるのだが、
    その代わり
    値を入れるときは普通の代入
    参照を代入する場合はsetステートメントと使い分ける必要がある。

    POINT 
      これに対して、C#は、すべてのクラスのスーパー・クラスを辿っていくと、
       最後にたどり着くobject型に、すべてのデータの参照を格納できる。
       ちなみに、object は System.Object の別名。
       実際に
       整数、実数、文字列、Structs、クラスの5種類の情報を1個のobject型の配列に代入できる。  

 {
   public struct Structs1
   {}

   public class Class1
   {
     public static int Main(string[] args)
     {
       object [] test = new object[5];
       test[0] = (int)1;
       test[1] = (float)0.1;
       test[2] = (string)"Hello!";
       test[3] = new Structs1();
       test[4] = new Class1();
       for( int i=0; i< 5; i++ )
       {
         Console.WriteLine( "Class={0}, Value={1}",
           test[i].GetType().FullName, test[i].ToString() );
       }
       return 0;
     }
   }
 }
これまでの常識では、「object」はクラスであり、クラスは参照型なのだから 整数値などは代入できないはずだ。 配列testの各要素について、データ型と値を表示する。 GetType( )はデータ型に関する情報を取得するためのメソッドで 「GetType( ).FullName」によって そのデータ型のネームスペースを含むフルネームが得られる。 GetType( )はデータ型を得るためのメソッドでどのオブジェクトでも使用できる。 FullName は、そのデータ型の namespace を含むフルネームを得るためのプロパティ。 クラスや Structs に対して、ToString( )をオーバーライドせずに呼び出すと、 そのクラスや Structs の名前を返す機能が備わっている。 なぜなら、objectはクラスであり、クラスは参照型であって、値型とは違うからだ。 値型の整数が簡単に代入できるわけがない。 つまり、値型である整数、実数、文字列、Structs は object 型に代入できないのが筋なのである。 C#の「ボクシング(boxing)」と呼ばれる機能により値型を参照型に代入できる。 C#では、値型の値を object などの参照型に代入しようとすると、ボクシングという機能が自動的に挿入される。 ボクシングは、値型を包み込むクラスを自動的に生成する。 クラスは参照型なので、そのまま参照型として利用できる。 object 型の変数には、C#のあらゆる型のデータへの参照が代入できる。 これにより、どんなデータ型でも収納できる便利なクラスが非常に作りやすくなっている。 ボクシングとは要するに、暗黙のうちにクッションとなるオブジェクトを自動的に生成して、 そのなかに値型の値をコピーすることを意味する。 元々の値型の値を参照するわけではない。 また、あくまでボクシングはデータをコピーする行為であるため、 元のデータを変更しても、それが反映されない。 このようにボクシングは便利ではあるが、込み入った使い方をしようとすると、 トラブルの元になる場合もある。 値型と参照型の違いは、きちんと意識して利用するようにしよう。
___

■ アクセス修飾子

  DESC
    C++ とは異なり, すべてのメンバー明示的に設定する。
    つけなければデフォルトの private
    
  POINT
    隠蔽の時には異なるアクセス修飾子を指定できる。
    オーバーライドする場合はアクセス修飾子は変更できない。

    internal: CMP 単位のみアクセス可能. ( public, private, internal と併用可能. )
    「現在のコンパイル単位」という意味は、中間言語を理解するうえで重要。

    class Test {
      public void Write() {
        Console.WriteLine("test");
      }
    }

    class TokyoMM : Kitty {
      new private void Write() {
        Console.WriteLine("Tokyo mew mew");
      }
    }

    class Test {
      static void Main() {

        // CMP OK !
        // private にすること == そのメンバを隠蔽することに等しい
        // ので, Kitty::Write() にアクセスする.
        // C++ ではどうだろうか ? 
        TokyoMM obj = new TokyoMM();
        obj.Write();
      }
    }
C++ では CMP ERR になるらしい.
___

■ CustomAttribute


  DESC
    Class, Method に追加情報を与えるもの. 
    自分で新たな属性を作る場合は Attribute というクラスを継承して作成します。


  POINT
    NUnit は次のように解釈する。

    // [TestFixture]属性であるMyTestはテストクラスである
    [TestFixture]
    public class MyTest {

      // [Setup]属性であるInit()は各テストメソッドの開始前に呼ぶメソッドである
      [SetUp]
      public void Init() {
        // 各テストメソッドが呼ばれる前に行う処理
      }

      // [Test]属性をもつメソッドはテストメソッドである
      [Test]
      public void Test1() {
        // テスト処理
      }

      // [Test]属性をもつメソッドはテストメソッドである
      [Test]
      public void Test2() {
        // テスト処理
      }
    }
    namespace SampleCustomAttribute {

        // 自作したクラス用の属性 MyClassAttribute
        // -> なるほど ! 属性というクラスをつくるのか ^ ^/
        [AttributeUsage(AttributeTargets.Class)]
        class MyClassAttribute : Attribute {
           
            private string name;   // 名前 
            private bool isBool;   //  

            public MyClassAttribute(string name) {
                this.name = name;
            }

            // Accessor
            public string Name {
                get { return name; }
            }
            public bool IsBool {
                get { return isBool; }
                set { isBool = value; }
            }
        }
    }
namespace SampleCustomAttribute { // 自作したメソッド用の属性 MyMethodAttribute // [ AttributeUsage(...) ]は属性クラスの頭に必ずつける。 [AttributeUsage(AttributeTargets.Method)] class MyMethodAttribute : Attribute { private string name; public MyMethodAttribute(string name) { this.name = name; } public string Name { get { return name; } } } } (...) の中は、 クラスの属性の場合、AttributeTargets.Class メソッドの属性の場合、AttributeTargets.Method となります。 では、これらの属性を使ってみます。 namespace SampleCustomAttribute { [MyClass("this is HogeClass")] class HogeClass { [MyMethod("this is Method1.")] public void Method1() { Console.WriteLine("MyClass Method1()."); } } } HogeClass というクラスに属性 MyClassAttribute を、 Method1 に属性 MyMethodAttribute を付けました。 属性を付けるときは、 [MyClass("this is HogeClass")] のように Attribute を省略も [MyClassAttribute("this is HogeClass")] のようにAttributeを付けることもできる。 Attribute は省くのが一般的。 MyClassAttribute, MyMethodAttribute はコンストラクタで引数を取るので、 属性を付ける際に引数を渡しています。 もし MyClassAttribute のコンストラクタの引数がゼロだった場合、 [MyClassAttribute]となる。 また MyClassAttribute のプロパティ IsBool は以下のように設定できる。 namespace SampleCustomAttribute { // class に属性をつけた. [MyClass("this is HogeClass", IsBool=true)] class HogeClass { // Method に属性をつける。 [MyMethod("this is Method1.")] public void Method1() { Console.WriteLine("MyClass Method1()."); } } } IsBoolのようにsetを持つプロパティは、属性指定時に プロパティ名 = 値 という風に書くことができます。 コンストラクタの引数 - 必須の引数 setをもつプロパティ - 必須でない引数 という風に使い分けると良いでしょう。
___

■ 属性を読みだしてみる

属性を付けても、その属性を読み出せなければ意味がない。 主に リフレクション の機能を使う。 namespace SampleCustomAttribute { class AttributeSearch { // クラスのタイプから指定されている MyClassAttribute 属性を取り出す // class の種類を指定するらしい. public static MyClassAttribute[] GetMyClassAttribute( Type classType ) { object[] ary = classType.GetCustomAttributes( typeof(MyClassAttribute), false ); // cast して返す. return ( MyClassAttribute[] )ary; } // クラス名から指定されている MyClassAttribute 属性を取り出す public static MyClassAttribute[] GetMyClassAttribute(string className) { // 名前から, ClassType を取得する. // Type とは class の種類のことらしい. Type classType = Type.GetType( className ); // ↑のクラスに統合される. return GetMyClassAttribute( classType ); } // クラスのタイプとメソッド名から指定されている MyMethodAttribute 属性を取り出す public static MyMethodAttribute[] GetMyMethodAttribute(Type classType, string methodName) { MethodInfo info = classType.GetMethod(methodName); object [] ary = info.GetCustomAttributes(typeof(MyMethodAttribute), false); return (MyMethodAttribute[])ary; } } } 1 つのクラス、メソッドに対して同じ属性を複数指定できるので 戻り値は属性の配列となる。 これらを使って MyClassから属性を取り出すことができる。 // Type は ↑で定義した CustomAttributeClass MyClassAttribute[] myClassAttributes; // static Method を利用して取得する. myClassAttributes = AttributeSearch.GetMyClassAttribute( typeof(HogeClass) ); myClassAttributes = AttributeSearch.GetMyClassAttribute( "SampleCustomAttribute.MyClass" ); // Method を得る. MyMethodAttribute[] myMethodAttributes; myMethodAttributes = AttributeSearch.GetMyMethodAttribute(typeof(HogeClass), "Method1");
___

■ Generic


    Int型の固定サイズ配列のクラス。
    string など別の型をいれる場合は利用できないので
    似たような別クラスを用意することになる。
    public class IntFixedArray {

      public IntFixedArray( int num ) {
        _num = num;
        _data = new int[_num];
      }

      public void Set( int i, int value ) {  _data[i] = value;  }
      public int Get( int i ) {  return _data[i];  }

      int[] _data;
      int _num;
    }
object にすればすべての型を受け取ることができるが boxing(ボックス化) が発生して、データを取り出す時にどの型であるかチェックが必要。
    public class FixedArray {

      public FixedArray( int num ) {
        _num = num;
        _data = new object[_num];
      }

      public void Set( int i, object value ) {  _data[i] = value;  }
      public object Get( int i ) {  return _data[i];  }

      object[] _data;
      int _num;
    }
型をパラメータ化する。 これで, int, string, など型ごとにコンパイラがクラスを生成するので コードのひとつで済む。
    public class FixedArray< T> {

      public FixedArray( int num ) {
        _num = num;
        _data = new T[_num];
      }

      public void Set( int i, T value ) {
        _data[i] = value;
      }
      public T Get( int i ) {
        return _data[i];
      }

      T[] _data;
      int _num;
    }
    FixedArray< int> a = new FixedArray< int>(10);

    a.Set( 0, 1 );
    // コンパイル時に型チェックもしてもらえる。
    a.Set( 0, "foo" );


    FixedArray a = new FixedArray(10);
    a.Set( 0, "foo" );
    // ここで 値型から参照型への変換( boxing )が入る。
    a.Set( 0, 1 );
___

■ Image


___

■ bitmap

参照の追加 System.Drawing
    using System.Drawing;
    using System.Drawing.Imaging;

        {
            // Bitmapオブジェクト作成
            Bitmap b = new Bitmap("d:/test.bmp");

            // Colorの変更
            for (int i = 0; i <  b.Height; i++)
            {
                for (int j = 0; j <  b.Width; j++)
                {
                    Color c = Color.FromArgb(255, b.GetPixel(j, i).R, b.GetPixel(j, i).G, 255);
                    b.SetPixel( j, i, c );
                }
            }

            // ポインタ経由で直接変更をする。
            unsafe
            { 
                BitmapData data = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
                                                 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                // ピクセルデータの開始アドレス
               IntPtr pt = data.Scan0;  
                byte *p = (byte *)pt;
                for( int i=0; i< b.Width*b.Height; i++ ){
                    p[i] = 128;
                } 
            }

            // セーブ
            b.Save("d:/test_out.bmp");
            // PNG形式に変換して保存
            b.Save( "d:/test_out.png", System.Drawing.Imaging.ImageFormat.Png );
        }
___

■ ファイル


  file
    resx : リソースファイル ( build に必須 )

___

■ アイコン(ICON)





    アイコンを指定しておくと, alt-tab で表示されるアイコンを変更できる。


    まずアイコンファイルを作成して、プロジェクトにリソースとして組み込む。
    SolutionExplorer > RMB > Property > [ リソース ]タブ
左上のアイコンを選択して、 *.ico ファイルを指定して追加する。 リソースの名前は例として "test" とつけておく。 リソースに組み込んだアイコンはフォームのコンストラクタで設定する。
  namespace WindowsFormsApplication3
  {
    public partial class Form1 : Form

        public Form1()
        {
                Icon icon = WindowsFormsApplication3.Properties.Resources.test;
                this.Icon = icon;
        }
  }
___

■ 引数



  public static void Main(string[] args)
  {
    for(int i=0; i< args.Length; ++i) {
      Console.Write("{0}番目のコマンドライン引数は{1}です。\n", i, args[i] );
    }
  }




樋口了一★時計台アコースティックライブ★


外から自宅のパャRンの電源を入れて使う本 ネットワークを経由して外からサクサクパャRン操作


Subversion入門


イライラ解消!エクセル即効ワザ99 仕事が早く終わる完ぺき修得本


プログラマのための文字コード技術入門


外から自宅のパャRンの電源を入れて使う本 ネットワークを経由して外からサクサクパャRン操作


イライラ解消!エクセル即効ワザ99 仕事が早く終わる完ぺき修得本


Subversion入門


メールの超プロが教えるGmail仕事術


3分間ネットワーク基礎講座 世界一わかりやすいネットワークの授業

NINJAIDX 10