C# の練習，落書き帳


# 全体的なこと
O'Reilly プログラミング C# を元に勉強しながらテストコードなどを殴り書きしていくスタイル．

# 型推論型 var，for, foreach の使い方

型推論型 var の使い方は，Microsoft からガイドライン(https://msdn.microsoft.com/ja-jp/library/bb384061.aspx) に詳細が掲載されている．

1. var は，基本的に右辺が明確な時のみ利用する．
2. for, foreach では，初期化子に利用する
3. 上記以外は，var を用いない．例えば， ``String s = GetStringSomewhere();`` のような，メソッドから受け取るときは var は用いない．


## for, foreach の場合
for, foreach は比較的分かりやすい．とにかく __var__ で受け取れば良い．

```csharp
public static void Main(string[] args)
{
    // new List<Object> で型名が明確になっているので宣言側は var にする．
    // 何でも型は，Object で表現できる．
    var list = new List<Object>();
    // add value           CLR名           C#型
    list.Add("abc");    // System.String   (string)
    list.Add(true);     // System.Boolean  (bool)
    list.Add(100);      // System.Int32    (int)
    list.Add(200u);     // System.UInt32   (uint)
    list.Add(100.5f);   // System.Single   (float)
    list.Add(100.25d);  // System.Double   (double)
    list.Add(100.1m);   // System.Decimal  (decimal)

    String format = "| {0, 6}  |  {1, -15} |";
    Console.WriteLine(String.Format(format, "Value", "GetType"));
    Console.WriteLine(String.Format("|--------:|:-----------------|"));

    // 型推論 var による for, foreach
    //
    // for, foreach における添え字，要素型は var とする
    //    https://msdn.microsoft.com/ja-jp/library/bb384061.aspx

    // e.g. for
    for (var i = 0; i < list.Count; ++i)
    {
        Console.WriteLine(string.Format(format, list[i], list[i].GetType()));
    }

    Console.WriteLine(Environment.NewLine);

    // e.g foreach
    foreach (var l in list)
    {
        Console.WriteLine(string.Format(format, l, l.GetType()));
    }
}
```

結果(コンソール)．GetType は，CLR名を返すんだね．

```
|  Value  |  GetType         |
|--------:|:-----------------|
|    abc  |  System.String   |
|   True  |  System.Boolean  |
|    100  |  System.Int32    |
|    200  |  System.UInt32   |
|  100.5  |  System.Single   |
| 100.25  |  System.Double   |
|  100.1  |  System.Decimal  |


|    abc  |  System.String   |
|   True  |  System.Boolean  |
|    100  |  System.Int32    |
|    200  |  System.UInt32   |
|  100.5  |  System.Single   |
| 100.25  |  System.Double   |
|  100.1  |  System.Decimal  |
```

## ローカル変数のとき
ローカル変数のときは，少し複雑になる．

### 整数リテラルの場合
整数リテラル(整数の即値)で宣言したときは，基本的には 32bit 符号付き整数になる．
(まあ，当然と言えば当然かも)

```csharp
// 整数リテラルで宣言する
//
// これら全ては，Int32, Int64, UInt64 で型推論を試みることを確認する．
// できるだけ Int32 で済ませようとし，Int32 以上ならば Int64，
// それ以上ならば UInt64 になることを確認する．
//
// すなわち，優先度は以下の通りとなる
//   1.   int,  Int32  32bit 符号付き整数
//   2.  long,  Int64  64bit 符号付き整数
//   3. ulong, UInt64  64bit 符号なし整数
var intention_is_sbyte = 128;                  // sbyte  = Int8 のつもり
var intention_is_short = 32767;                // short  = Int16 のつもり
var intention_is_int   = 65536;                // int    = Int32 のつもり
var intention_is_long  = 4294967296;           // long   = Int64 のつもり
var intention_is_ulong = 9223372036854775808;  // ulong = UInt64 のつもり

// 標準出力用のフォーマット
var format = "| {0, -19}| {1, 26:N0} | {2, -15}|";

Console.WriteLine(String.Format(format, "value name",
                  "value", "get type"));

Console.WriteLine("|:-------------------|" +
                  "---------------------------:|" +
                  ":---------------|");

Console.WriteLine(String.Format(format, "intention_is_sbyte",
                  intention_is_sbyte, intention_is_sbyte.GetType()));

Console.WriteLine(String.Format(format, "intention_is_short",
                  intention_is_short, intention_is_short.GetType()));

Console.WriteLine(String.Format(format, "intention_is_int",
                  intention_is_int, intention_is_int.GetType()));

Console.WriteLine(String.Format(format, "intention_is_long",
                  intention_is_long, intention_is_long.GetType()));

Console.WriteLine(String.Format(format, "intention_is_ulong",
                  intention_is_ulong, intention_is_ulong.GetType()));
```

結果．sbyte, short など，int32 以内に収まる整数型は int32 になる．
```
| value name         |                      value | get type       |
|:-------------------|---------------------------:|:---------------|
| intention_is_sbyte |                        128 | System.Int32   |
| intention_is_short |                     32,767 | System.Int32   |
| intention_is_int   |                     65,536 | System.Int32   |
| intention_is_long  |              4,294,967,296 | System.Int64   |
| intention_is_ulong |  9,223,372,036,854,775,808 | System.UInt64  |
```

では，sbyte や short の範囲のときはどうすればいいか．__var__ をやめればよい．
おとなしく，sbyte や short で宣言しろってことですね．

次の場合は混乱するかもしれない．
```csharp
Int16 value      = 16500;
var whatItWillBe = value + 1;
Console.WriteLine(whatItWillBe);
Console.WriteLine(whatItWillBe.GetType());
```

結果
```
16501
System.Int32
```

はい，int になりました．整数リテラルが int なので，short + int = int となったんでしょうね．
他のプログラミング言語でも 大きい方の型を採用することがほとんどです．
なので，以下の例はコンパイルエラーが出ます．

```csharp
public static void someFunction(byte b)
{
    Console.WriteLine("0x{0,0:X}", b);
}

public static void Main(string[] args)
{
    byte value         = 128;
    var convertedToInt = value + 1;
    someFunction(convertedToInt);     // convertedToInt は int型なので渡せない．
    someFunction(value + 1);          // 当然，これもエラーになる
    someFunction((byte)(value + 1));  // キャストすれば OK．
}
```

このように，便利だからと言ってなんでも __var__ を使うと，コンパイルエラーとなります．
引数に渡す変数は，明確に関数の引数に宣言されている変数型を合わせるべきです．

```csharp
public static void Main(string[] args)
{
    byte value           = 128;
    byte  notBeConverted = (byte)(value + 1);
    someFunction(notBeConverted);     // 大丈夫
}
```