ステップ 7 ハンズオン 「Visual Studio 2005 Team System によるアプリケーション品質の向上」
第 2 回 品質の高いコード作成 (1) - その 1
静的コード分析の設定と実施
NRIラーニングネットワーク
荒木 達也
最終更新日 2007 年 10 月 10 日
| 目標 |
Visual Studio Team System を利用した高品質コードの作成 |
| 使用技術 |
- Visual Studio Team System / C#
- .NET Framework 2.0
|
| 取り上げるトピックス |
|
| 前提知識 |
|
| 関連記事 |
|
静的コード分析
今回は、引数として渡される商品コードをもとに SQL (SELECTステートメント) を発行し、該当する商品名を結果として返す GetProductName メソッドを、コード分析の対象とします。 (このプログラムは、Step1 で作成します)

図1.コード分析するサンプルコンポーネント
| 実装する処理 |
メソッド |
呼び出し対象 |
| 商品名取得 |
GetProductName |
|
なお、今回コード分析する規則 (CODE_ANALYSIS定数) は以下の通りです。
| 規則名 |
詳細の規則 |
| セキュリティ規則 |
すべて |
| パフォーマンス規則 |
すべて |
| 信頼性の規則 |
すべて |

Visual Studio 2005 を起動し、メニューより [ ファイル ] - [ 新規作成 ] - [ プロジェクト ] の順に選択し、新しいプロジェクトを作成します。
| プロジェクトの種類 |
テンプレート |
プロジェクト名 |
| C# - Windows |
クラス ライブラリ |
MyLibs |

図2. プロジェクト MyLibs" の作成
作成したプロジェクトの既定のクラス Class1 を MyComp1 に変名し、データベースへ商品名を問い合わせる GetProductName メソッドを作成します。
public String GetProductName(int productId)
{
String result;
SqlConnection cn = new SqlConnection(constr);
SqlCommand cmd = new SqlCommand();
cmd.Connection = cn;
cmd.CommandText = "SELECT ProductName FROM Products" +
" WHERE ProductID = " + productId.ToString();
try
{
cn.Open();
result = (String)cmd.ExecuteScalar();
cn.Close();
cmd.Dispose();
cn.Dispose();
}
catch
{
result = "";
}
return (result);
}

プログラム記述後、既定の環境 (静的なコードが無効な状態) でビルドします。
メニューより [ ビルド ] - [ MyLibs のビルド ] を選択します。
ビルドの結果が表示され、正常にコンパイルされた旨通知されます。

図3. ビルド結果

静的コード分析を有効にします。
メニューより [ プロジェクト ] - [ MyLibs のプロパティ ] を選択します。
[ MyLibs ] プロジェクトプロパティ のダイアログにて、[ コード分析 ] タブを選択後、
- コード分析の有効化 (CODE_ANALYSIS 定数を定義)
- セキュリティ規則
- パフォーマンス規則
- 信頼性の規則
の 4 つのチェックボックスを ON にします。

図4. コード分析の有効化

静的なコード分析が有効な状態でビルドします。
メニューより [ ビルド ] - [ MyLibs のビルド ] を選択します。
ビルドの結果が表示され、正常にコンパイルされた旨通知されます。

図5.ビルド結果
コンパイルの結果と共に、コード分析の結果も表示されます。
詳細を確認するには、メニューより [ 表示 ] - [ エラー一覧 ] を選択します。

図6.コード分析の結果
分析の結果、以下の 4 か所について警告を受けていることが分ります。
- 信頼性の規則 で 2 か所
変数 cmd と変数 cn の後処理 (オブジェクトの破棄) が行われてません。特に cn については、try ブロックの中で、後処理に相当する Close メソッドを呼び出していますが、この呼び出しの前に例外が発生すると、catch ブロックに制御が移ってしまい、後処理ができません。
このコードには、後処理が行われない処理フロー (コードパス) が存在しています。
- パフォーマンス規則 で 1 か所
非 static メソッドよりも、static メソッド (静的メソッド) のほうが、内部的なパラメータが一つ少なく負荷が減ります。static メソッドで済む場合には、static メソッドを使用するようにします。
- セキュリティ規則 で 1 か所
SQLインジェクションの危険性があります。つまり、SQL 文の文字列の WHERE 句の後に、不適切なコマンド文字列を連結して、不正な操作をさせる可能性があります。WHERE 句の後ろの抽出条件は、単純な文字列連結ではなく、パラメータ化するなど、特定の項目しか代入できないようにします。

警告を受けたコードを修正します。
public static String GetProductName(int productId)
{
String result;
SqlConnection cn = new SqlConnection(constr);
SqlCommand cmd = new SqlCommand();
cmd.Connection = cn;
cmd.CommandText = "SELECT ProductName FROM Products" +
" WHERE ProductID = @ProductID";
SqlParameter parmProdID = cmd.Parameters.Add
("@ProductID",SqlDbType.Int);
parmProdID.Value = productId;
try
{
cn.Open();
result = (String)cmd.ExecuteScalar();
}
catch
{
result = "";
}
finally
{
cn.Close();
cmd.Dispose();
cn.Dispose();
}
return (result);
}
修正は、以下の 4 か所です。
- コネクション、コマンドオブジェクトの確実な破棄 (2か所)
例外処理に finally ブロックを追加し、例外発生の有無に関わらず、確実にコネクション、コマンドオブジェクトを破棄します。
- メソッドの static 化
つまり、静的メソッドにします。
- SQL ステートメントのパラメータ化
WHERE 句の抽出条件をパラメータ化し、整数のみ受け渡せるようにします。

もう一度、静的なコード分析が有効な状態でビルドします。
メニューより [ ビルド ] - [ MyLibs のビルド ] を選択します。
ビルドの結果が表示され、正常にコンパイルされた旨通知されます。

図7.ビルド結果
コンパイルの結果と共に、コード分析の結果も表示されます。
コード修正後の分析結果は良好 (警告は 0 (ゼロ)) です。

このプログラムに対するコード分析は完了したので、静的なコード分析は “無効” に戻しておきます。
メニューより [ プロジェクト ] - [ MyLibs のプロパティ ] を選択します。
[ MyLibs ] プロジェクトプロパティ のダイアログにて、[ コード分析 ] タブを選択後、
- コード分析の有効化 (CODE_ANALYSIS 定数を定義)
のチェックボックスを OFFにします。
次に、「その 2 : プロファイリング 」 について説明します。
|