Visual Studio 2013の新機能を活用して、デバッグしているときにメソッドの戻り値を調べる方法を解説する。
powered by Insider.NET
デバッグ中にメソッドの戻り値が確認できなくて、やむなく戻り値を変数に代入するようにコードを書き直したという経験はないだろうか? そんなとき、Visual Studio 2013(以降、VS 2013)の新機能が役に立つ。Windowsストアアプリに限った話ではないのだが、今回はVS 2013でデバッグしているときにメソッドの戻り値を調べる方法を解説する。
Win 8.1用のWindowsストアアプリ(以降、Win 8.1アプリ)を開発するには、Win 8.1とVisual Studio 2013(以降、VS 2013)が必要である。本稿ではOracle VM VirtualBox上で64bit版Windows 8.1 Pro(日本語版)とVisual Studio Express 2013 for Windows(日本語版)*1を使用してプログラミングしている。
*1 マイクロソフト公式ダウンロードセンターの「Microsoft Visual Studio Express 2013 for Windows」から無償で入手できる。
あるメソッドを呼び出す際に、別のメソッドの戻り値をそのまま引数に使っているため、デバッグ時にメソッドの戻り値が確認できないパターンがある。例えば次のようなコードだ。
private void Test01()
{
var result = Add(1, Mul(2, 3)); // ←ここでブレークして、ステップオーバー
}
private int Add(int x, int y)
{
return x + y;
}
private int Mul(int x, int y)
{
return x * y;
}
Private Sub Test01()
Dim result = Add(1, Mul(2, 3)) ' ←ここでブレークして、ステップオーバー
End Sub
Private Function Add(x As Integer, y As Integer) As Integer
Return x + y
End Function
Private Function Mul(x As Integer, y As Integer) As Integer
Return x * y
End Function
上のコードをデバッグしていてresultに入った値が正しくないことが判明した場合、その原因がAddメソッドにあるのかMulメソッドにあるのか、分かるだろうか? この例ではAddメソッドもMulメソッドもその中をデバッグ可能だが、それができないとしたらどうだろう?
これまでなら、コードを書き換えてデバッグし直すしかなかった(次のコード)。
var temp = Mul(2, 3);
var result = Add(1, temp);
Dim temp = Mul(2, 3)
Dim result = Add(1, temp)
このように書き換えることで、デバッグ時にMulメソッドの戻り値を調べられるようにして、バグの原因がAddメソッドにあるのかMulメソッドにあるのか切り分けていたことだろう。
ところがVS 2013では、最初のコードのままで、Mulメソッドの戻り値が分かるのだ。コメントに「ここでブレークして、ステップオーバー」とある行にブレークポイントを置いてデバッグ実行を開始しよう。ブレークしたら、1回ステップオーバーする(VS 2013のメニューバーでは[デバッグ]−[ステップ オーバー])。すると次の画像のようになっているはずだ。
ここで[自動変数]ウィンドウを見てほしい(表示されていないときはメニューバーの[デバッグ]−[ウィンドウ]−[自動変数]を選択すると表示される)。上の画像を拡大して次に示す。
[自動変数]ウィンドウ(画像の左側)の1行目に、「(前略)Mulが返されました*2」として、値が「6」と報告されている。これがMulメソッドの戻り値なのである。そして、2行目には「(前略)Addが返されました」として、Addメソッドの戻り値「7」が表示されている。このように、ステップオーバーした行で呼び出したメソッドの戻り値が全て表示されるのだ。
また、[イミディエイト ウィンドウ](表示されていないときはメニューの[デバッグ]−[ウィンドウ]−[イミディエイト]を選択すると表示される)で、「$ReturnValue」と入力してEnterキーを押してみると、「7」と表示される。これは、最後に呼び出したメソッド(今の場合はAddメソッド)の戻り値である。さらに、[イミディエイト ウィンドウ]では、「$ReturnValue1」が[自動変数]ウィンドウに表示されているメソッドの戻り値の1行目(=Mulメソッドの戻り値「6」)、「$ReturnValue2」が同じく[自動変数]ウィンドウの戻り値の2行目(=Addメソッドの戻り値「7」)になる。
なお、ステップオーバーした次の行(=最初の画像で黄色の矢印がある行)にブレークポイントを置いて、そこまで一気に実行してしまうと、戻り値の表示は出ないので注意してほしい。
*2 「(前略)Mulが返されました」というのは誤訳。英語版では「(前略)Mul returned」(Mulが返しました)であり、日本流に訳すなら「Mulの戻り値」。
以上のように、VS 2013の戻り値を表示する機能は素晴らしいものなのだが、残念なことにasync(VBではAsync)キーワード付きのメソッド(以降、非同期メソッド)を呼び出した場合は機能しない。例えば次のようなコードだ。
private async Task Test02Async()
{
// async/awaitが入ると、メソッドの戻り値は表示されない。
int result;
// 非同期メソッドの中から呼び出しても
result = await Task.Run(() =>
Add(1, Mul(2, 3)) // ←ここでブレークして、ステップオーバー
);
// 非同期メソッドを呼び出しても
result = Add(2, await MulAsync(3, 4)); // ←ここでブレークして、ステップオーバー
}
private async Task<int> MulAsync(int x, int y)
{
return await Task.Run<int>(() => x * y);
}
Private Async Function Test02Async() As Task
' async/awaitが入ると、メソッドの戻り値は表示されない。
Dim result As Integer
' 非同期メソッドの中から呼び出しても
result _
= Await Task.Run(Function() _
Add(1, Mul(2, 3))) ' ←ここでブレークして、ステップオーバー
' 非同期メソッドを呼び出しても
result = Add(2, Await MulAsync(3, 4)) ' ←ここでブレークして、ステップオーバー
End Function
Private Async Function MulAsync(x As Integer, y As Integer) As Task(Of Integer)
Return Await Task.Run(Of Integer)(Function() x * y)
End Function
上のコードで、先ほどと同様に「ここでブレークして、ステップオーバー」のコメントがある行にブレークポイントを設置して、ステップオーバーしてみても、メソッドの戻り値は報告されないのだ。
メソッドの戻り値に対してまたメソッドを呼び出す「メソッドチェーン」というコードの書き方がある。例えば次のようなコードだ。
var result = "$$$Test String%%%"
.TrimStart('$')
.TrimEnd('%')
.ToUpperInvariant()
.Replace(' ', '-')
;
Dim result = "$$$Test String%%%" _
.TrimStart("$") _
.TrimEnd("%") _
.ToUpperInvariant() _
.Replace(" ", "-")
メソッドチェーンは、処理の流れを分かりやすくしかも簡潔に記述できるのがよいのだが、デバッグ時に途中の結果を確認しようとすると今までは大変だった。メソッドチェーンで簡潔に書かれたコードを、ワーク変数を使った冗長なコードに書き直さねばならなかったのだ。ところが、VS 2013では次の画像のようにメソッドチェーンの途中の戻り値が表示される。
[自動変数]ウィンドウには、TrimStartメソッドの戻り値を先頭に、メソッドチェーンで呼び出した順に戻り値が4つ表示されている。それらと同じものを、[イミディエイト ウィンドウ]では「$ReturnValue1」〜「$ReturnValue4」として表示できる。
メソッドチェーンでもLINQの場合は少々異なり、VBでは残念ながら戻り値が表示されないようだ。例えば次のようなコードだ。
var result = Enumerable.Range(1, 100)
.Where(n => n % 2 == 1)
.Sum();
Dim result = Enumerable.Range(1, 100) _
.Where(Function(n) n Mod 2 = 1) _
.Sum()
これをステップオーバーしたとき、C#では途中の戻り値が表示されるが、VBでは表示されない(次の画像)。
LINQの場合、VBの[自動変数]ウィンドウの1行目と2行目の[値]と[型]の欄は簡略な表示になっていて、実際には何なのかよく分からない。[イミディエイト ウィンドウ]で「$ReturnValue1」と「$ReturnValue2」を表示してみても、「展開すると、コレクションが処理されます」と出てくるだけで、実体は調べられないようだ。
C#も[自動変数]ウィンドウでは途中の戻り値の実際の値は分からない。しかし、[イミディエイト ウィンドウ]で例えば「$ReturnValue2.ToArray()」を評価すれば、Whereメソッドで絞り込まれた結果(=1,3,5,7,…,99)が表示される(VBでは同様にしても何も表示されない)。
VS 2013ではデバッグ実行中にステップオーバーしたときに、その行で呼び出したメソッドの戻り値が全て表示されるようになった(Windowsストアアプリに限らない)。ただし、非同期メソッドでは表示されない。また、VBではLINQでも途中の戻り値を調査できない。
このVS 2013の新機能については、次のドキュメントも参照してほしい。
Copyright© Digital Advantage Corp. All Rights Reserved.