全体的なこと
O'Reilly プログラミング C# を元に勉強しながらテストコードなどを殴り書きしていくスタイル.
型推論型 var,for, foreach の使い方
型推論型 var の使い方は,Microsoft からガイドライン(https://msdn.microsoft.com/ja-jp/library/bb384061.aspx) に詳細が掲載されている.
- var は,基本的に右辺が明確な時のみ利用する.
- for, foreach では,初期化子に利用する
- 上記以外は,var を用いない.例えば,
String s = GetStringSomewhere();
のような,メソッドから受け取るときは var は用いない.
for, foreach の場合
for, foreach は比較的分かりやすい.とにかく var で受け取れば良い.
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 符号付き整数になる.
(まあ,当然と言えば当然かも)
// 整数リテラルで宣言する
//
// これら全ては,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 となったんでしょうね.
他のプログラミング言語でも 大きい方の型を採用することがほとんどです.
なので,以下の例はコンパイルエラーが出ます.
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 を使うと,コンパイルエラーとなります.
引数に渡す変数は,明確に関数の引数に宣言されている変数型を合わせるべきです.
public static void Main(string[] args)
{
byte value = 128;
byte notBeConverted = (byte)(value + 1);
someFunction(notBeConverted); // 大丈夫
}