筆者の恥ずかしい失敗談を晒すエントリです。
まずはこちらをご覧ください。
問題
このとき isInt と tmp がコンソールに出力する値はなんでしょう?
using System;
public class Hello
{
public static void Main()
{
int tmp = 10;
bool isInt = Int32.TryParse("six", out tmp);
Console.WriteLine(isInt);
Console.WriteLine(tmp);
}
}
正解
変数 | 出力される値 |
---|---|
isInt | false |
tmp | 0 |
そんなのは当たり前だ、という賢明な諸氏は特にこの記事から得るものはないのでそっとタブを閉じてやってください。
誤答
で、そんなのは当たり前ではなかった筆者の恥晒し誤答はこちら。
変数 | 出力される値 |
---|---|
isInt | false |
tmp | 10 |
isInt が false になるのは期待どおりですが、問題が out 変数に指定していた tmp で、こちらは 10 が出力される、と思い込んでいました。数値に変換できない値を渡した場合であっても out に代入はされる んですね。なので tmp に予め 10 を格納しておいても全く意味がないということです。
公式
変換に失敗した場合は 0 を格納します。
しっかり書かれてました。みなさん、公式ドキュメントはよく読みましょう(ブーメラン)
ところで 0 はいつ格納されているのか
ちょっと気になったので軽くソースを見てみることにしました。
この記事を参考にさせていただきました。
https://qiita.com/Marimoiro/items/e255eecc265cd5a45025
int
は言わずもがな System.Int32
のエイリアスです。
なので今回見たいのは System.Int32
クラスのドキュメントになります。さっそく見てみます。
https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/language-specification/types#simple-types
System.Int32.TryParse
実際には Number.TryParseInt32
が実質的な処理を行っているようですね。というわけで次へ進みます。
System.Number.TryParseInt32
https://referencesource.microsoft.com/#mscorlib/system/number.cs,958cb8bc00d00a94
メソッドもそうなんですが、Numberクラス自体も internal であるため、基本はプログラマーが呼び出して使うようなクラスではなさそうですね。んで、問題の out 引数への代入はメソッド開始から3行目で早々に行われていることがわかります。
result = 0
中の実装まで見なくてもメソッドの挙動から明らかではありますが、out を付けた引数は参照渡しになります。
なのでこの代入によって先ほどの問題の tmp = 10
が書き換えられていることは間違いなさそうです。
追記
@albireo さんからのコメントを参考に、out引数を持つメソッドを自作して確認してみたところ、そもそもout引数に対してメソッド内で何も値を代入しないとコンパイルエラーになることがわかりました。少なくともout引数が何らかの書き換えが行われていることは確実と言えます。
void hoge(ref int refValue, out int outValue)
{
//これはOK
if (refValue < 0) {
refValue = 0;
}
if (outValue < 0) { //後述のエラー1
outValue = 0;
Console.WriteLine(outValue);//これはエラーにならない
}
//後述のエラー2
}
エラー1
割り当てのない out パラメーター 'outValue' の使用です。
エラー2
out パラメーター 'outValue' はコントロールが現在のメソッドを抜ける前に
割り当てられる必要があります。
これでもう同じ失敗は繰り返さないでしょうw
最後まで読んで下さりありがとうございました。