DataTable から値を引っ張る方法あれこれ このエントリーをはてなブックマークに追加

C# で DB を扱う場合などで、よく DataTable を使うことになると思うのですが、こいつから値を取り出すときの書き方が人によって結構まちまちだな、と思うことが結構あるので、ちょっとおさらいしてみることにします。

今回は割と初歩的な内容になります。あと、記事中のコードはテストしてないので動作を保証できません。

方法1. 普通にキャストする

DataTable table = view.Select();
foreach (var row in table)
{
    var seq = (int)row["HOGE_CD"];
    var name = (string)row["HOGE_NAME"];

    var remark = row["HOGE_REMARK"].GetType() == typeof(string) ?
        (string)row["HOGE_REMARK"] : null;

    // 処理 ...

}

NULL を許容するフィールドについては型チェックを行う必要がある、というのがポイントになります。 NULL 値は null ではなく DBNull.Value として入ってくるので、そのままキャストしようとすると InvalidCastException 例外 が送出される場合があるからです。

方法2. as 演算子を使う

DataTable table = view.Select();
foreach (var row in table)
{
    var seq = row["HOGE_CD"] as int?;
    var name = row["HOGE_NAME"] as string;
    var remark = row["HOGE_REMARK"] as string;

    // 処理 ...

}

as 演算子は、型キャストが可能ならばキャストを行い、不可能な場合は null を返すという、最近ありがちなフレームワークでの頻繁に down cast を要求するような実装においては非常に便利な演算子です。 C++ で言うところの dynamic_cast 演算子に似ています (なので、個人的にはなるべく避けたい、頼り切りたくない概念なのですが…)。

普通に型キャストを行う方法に比べると、 NULL 許容フィールドに対しても同じように書けるため、非常にすっきりします。しかし、 int のような据え置き型などの、 null を許容しない型 (.NET では構造体と呼ばれているもの) に対して使うことは出来ません。その為、 int 型の値を取得する際には、指定する型を int? 型としているのがポイントです。これは、以下のように書くのと同じ意味になります。

    var seq = row["HOGE_CD"] as Nullable<int>;

Nullable<T> 構造体は null を許容する特殊な構造体という位置づけで、通常の値型の値と同じように四則演算や比較演算を行うことも出来たりします。但し、元の値型が要求される場面ではそのまま渡すことは出来ないので、 null チェックをしてから Value プロパティを使う必要があります。

    if (seq.HasValue)   // null ではないことを確認
        MyMethod(seq.Value, name, remark);
    else
    {
        // nul だった場合の処理 ...
    }

方法3. object.ToString() メソッドを使う

DBNull.ToString() が常に string.Empty を返すことを逆手にとり、元の値の型に関係なく手当たり次第に ToString() を呼んでしまえという考え方もあったりします。そのまま文字列で出力することの多い ASP.NET での開発などでは割とよく見かける手法です。

DataTable table = view.Select();
foreach (var row in table)
{
    var seq = row["HOGE_CD"].ToString();
    var name = row["HOGE_NAME"].ToString();
    var remark = row["HOGE_REMARK"].ToString();

    // 処理 ...

}

数値が入るフィールドの値をそのまま文字列として出力したいようなケースでは便利です。但し、 DateTime 型のように、そのまま ToString() してしまうと必ずしも期待した通りの表示にならないものもあるので、注意が必要です。

文字列型のフィールドの場合、単に出力する用途であれば null が許容されるケースが殆どなので (文字列連結演算でも null の混在は許容されています)、 as 演算子を用いる方法の方が適切かも知れません。そうでない場合は、それこそ NULL 値をチェックして分岐するようなコーディングが必要なことが殆どであるハズなので、やはり as 演算子を用いる方法の方が適切であるといえます。

方法4. object 型のまま用いる

ToString() メソッドの利用にも通じるのですが、 例えば値の比較にのみ用いるので object.Equal() メソッドを呼べばいいか、みたいな使い方もあり得ます。

DataTable table = view.Select();
foreach (var row in table)
{
    // ...

    // 特定のステータスのレコードに対してのみ、何か特別な処理をさせたい場合など
    if (row["HOGE_STATUS"].Equal(MyConstant.HogeStatus.SpecialState))
    {
        // 処理...
    }
}

あるいは、そもそも object 型の値を要求するようなメソッドやプロパティに渡して用いるようなケースでは、わざわざ型キャストしてやる必要もないでしょう。 StringBuilder を用いて値を連結して使う場合なんかもそうですね。

まとめ

どの方法にもそれぞれ利点はあるので、ケースバイケースで使い分けると良いのではないかと思います。指針としてはこんな感じになるでしょうか。

方法 コード例 有効な用途 注意点
普通に型キャスト var seq = (int)row["HOGE_CD"];
var status = row["HOGE_STATUS"].GetType() == typeof(int) ? (int)row["HOGE_STATUS"] : -1;
null を許容しない型の値を取り出して評価したい場合。
または NULL 値を許容しないフィールドであることが分かっている場合。
NULL 値が入ってくるフィールドの場合、NULL チェックを怠ると型キャストに失敗して例外が送出されることがある。
as 演算子 var seq = row["HOGE_CD"] as int?;
var name = row["HOGE_NAME"] as string;
この書き方が煩雑ではなく、 Nullable<T> 構造体を用いるのが嫌でなければ、多くのケースで適当。
null を許容する string 型の場合は特に有効。
null を許容しない int 型や DateTime 型などの値の場合には、 Nullable<T> 構造体を用いた型 (int? など) で値を扱う必要がある。
object.ToString() メソッド hogeSeqNo_Label.Text = row["HOGE_SEQ"].ToString(); とにかく文字列として扱いたい場合。
そのまま object として扱う object.Equal() メソッドを用いた値の比較。
StringBuilder の利用等、 object 型の引数が要求されるケース。
object 型のままで済む場合。

2013 年 6 月 10 日 by 村山 俊之

タグ: ,

コメントをどうぞ