連載
» 2014年01月30日 14時20分 UPDATE

WinRT/Metro TIPS:Visual Studio 2013の新機能を使って、デバッグ時にメソッドの戻り値を調べるには?[Windows 8.1ストアアプリ開発]

Visual Studio 2013の新機能を活用して、デバッグしているときにメソッドの戻り値を調べる方法を解説する。

[山本康彦(http://www.bluewatersoft.jp/),BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 デバッグ中にメソッドの戻り値が確認できなくて、やむなく戻り値を変数に代入するようにコードを書き直したという経験はないだろうか? そんなとき、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

メソッドの戻り値をそのまま引数に使っているコード(上:C#、下:VB)

 上のコードをデバッグしていて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)

従来はコードを書き換えてデバッグしていた(上:C#、下:VB)
ワーク変数を導入し、Mulメソッドの戻り値を変数に代入してから使うようにコードを書き換えてデバッグしていた。

 このように書き換えることで、デバッグ時にMulメソッドの戻り値を調べられるようにして、バグの原因がAddメソッドにあるのかMulメソッドにあるのか切り分けていたことだろう。

 ところがVS 2013では、最初のコードのままで、Mulメソッドの戻り値が分かるのだ。コメントに「ここでブレークして、ステップオーバー」とある行にブレークポイントを置いてデバッグ実行を開始しよう。ブレークしたら、1回ステップオーバーする(VS 2013のメニューバーでは[デバッグ]−[ステップ オーバー])。すると次の画像のようになっているはずだ。

コメントに「ここでブレークして、ステップオーバー」とある行をステップオーバーしたところ(上:C#、下:VB)
コメントに「ここでブレークして、ステップオーバー」とある行をステップオーバーしたところ(上:C#、下:VB) コメントに「ここでブレークして、ステップオーバー」とある行をステップオーバーしたところ(上:C#、下:VB)
その次の行で実行が止まっている(左側に黄色の矢印がある行)。

 ここで[自動変数]ウィンドウを見てほしい(表示されていないときはメニューバーの[デバッグ]−[ウィンドウ]−[自動変数]を選択すると表示される)。上の画像を拡大して次に示す。

上の画像の[自動変数]ウィンドウと[イミディエイト ウィンドウ]の部分(上:C#、下:VB)
上の画像の[自動変数]ウィンドウと[イミディエイト ウィンドウ]の部分(上:C#、下:VB) 上の画像の[自動変数]ウィンドウと[イミディエイト ウィンドウ]の部分(上:C#、下:VB)

 [自動変数]ウィンドウ(画像の左側)の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の戻り値」。


asyncメソッドでは機能しない

 以上のように、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

非同期メソッドを含むパターンではメソッドの戻り値を取得できない(上:C#、下:VB)

 上のコードで、先ほどと同様に「ここでブレークして、ステップオーバー」のコメントがある行にブレークポイントを設置して、ステップオーバーしてみても、メソッドの戻り値は報告されないのだ。

メソッドチェーンでは特に役に立つ

 メソッドの戻り値に対してまたメソッドを呼び出す「メソッドチェーン」というコードの書き方がある。例えば次のようなコードだ。

var result = "$$$Test String%%%"
              .TrimStart('$')
              .TrimEnd('%')
              .ToUpperInvariant()
              .Replace(' ', '-')
             ;

Dim result = "$$$Test String%%%" _
             .TrimStart("$") _
             .TrimEnd("%") _
             .ToUpperInvariant() _
             .Replace(" ", "-")

メソッドチェーンになっているコード(上:C#、下:VB)
文字列「$$$Test String%%%」に対して、TrimStartメソッドで先頭の「$」を削除し、次にTrimEndメソッドで末尾の「%」を削除し、次にToUpperInvariantメソッドで大文字に変換し、次にスペースを「-」に置換している。

 メソッドチェーンは、処理の流れを分かりやすくしかも簡潔に記述できるのがよいのだが、デバッグ時に途中の結果を確認しようとすると今までは大変だった。メソッドチェーンで簡潔に書かれたコードを、ワーク変数を使った冗長なコードに書き直さねばならなかったのだ。ところが、VS 2013では次の画像のようにメソッドチェーンの途中の戻り値が表示される。

メソッドチェーンになっているコードでステップオーバーした(上:C#、下:VB)
メソッドチェーンになっているコードでステップオーバーした(上:C#、下:VB) メソッドチェーンになっているコードでステップオーバーした(上:C#、下:VB)
上のコードの「result =」の行にブレークポイントを置いてデバッグ実行中にブレークさせ、1回ステップオーバーしたときの[自動変数]ウィンドウと[イミディエイト ウィンドウ]。

 [自動変数]ウィンドウには、TrimStartメソッドの戻り値を先頭に、メソッドチェーンで呼び出した順に戻り値が4つ表示されている。それらと同じものを、[イミディエイト ウィンドウ]では「$ReturnValue1」〜「$ReturnValue4」として表示できる。

LINQのチェーンでも(C#のみ)

 メソッドチェーンでも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()

LINQのメソッドチェーンになっているコード(上:C#、下:VB)
このコードは、1から100までの整数から、まずWhereメソッドで奇数を取り出し、次にSumメソッドでそれらの合計値を求めている。

 これをステップオーバーしたとき、C#では途中の戻り値が表示されるが、VBでは表示されない(次の画像)。

LINQのメソッドチェーンになっているコードでステップオーバーした(上:C#、下:VB)
LINQのメソッドチェーンになっているコードでステップオーバーした(上:C#、下:VB) LINQのメソッドチェーンになっているコードでステップオーバーした(上:C#、下:VB)
上のコードの「result =」の行にブレークポイントを置いてデバッグ実行中にブレークさせ、1回ステップオーバーしたときの[自動変数]ウィンドウと[イミディエイト ウィンドウ]。

 LINQの場合、VBの[自動変数]ウィンドウの1行目と2行目の[値]と[型]の欄は簡略な表示になっていて、実際には何なのかよく分からない。[イミディエイト ウィンドウ]で「$ReturnValue1」と「$ReturnValue2」を表示してみても、「展開すると、コレクションが処理されます」と出てくるだけで、実体は調べられないようだ。

 C#も[自動変数]ウィンドウでは途中の戻り値の実際の値は分からない。しかし、[イミディエイト ウィンドウ]で例えば「$ReturnValue2.ToArray()」を評価すれば、Whereメソッドで絞り込まれた結果(=1,3,5,7,…,99)が表示される(VBでは同様にしても何も表示されない)。

まとめ

 VS 2013ではデバッグ実行中にステップオーバーしたときに、その行で呼び出したメソッドの戻り値が全て表示されるようになった(Windowsストアアプリに限らない)。ただし、非同期メソッドでは表示されない。また、VBではLINQでも途中の戻り値を調査できない。

 このVS 2013の新機能については、次のドキュメントも参照してほしい。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。