none
DataTable への配列オブジェクトの取り扱いにてVB.net 2003 とVB.NET 2013 では求められる結果が違う!?? RRS feed

  • 質問

  • VB.Net  2003(32ビットOS Windows XP) から VB.NET 2013 64ビットOS(Windows 8) への移行作業を行っています。

    以下のコードで VB.NET 2013で エラーにはなりませんが 求められる結果が正しくありません。_______________________________________________________________

    Public Class Form1

        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
            Dim DT As New DataTable

            Dim ar1 As New ArrayList
            Dim stary As String() = {"123", "456"}

            Dim I AS integer

            For I = 0 To stary.Length - 1

                DT.Columns.Add()
            Next
            ar1.Add(stary) 
            DT.Rows.Add(ar1(0))

        End Sub
    End Class

    _________________________________________________________________________________________

    本来であれば DataTable の 内容は

       Dt(0).ITem(0)   =>    "123"
       Dt(0).ITem(1)   =>    "456"

    になるはずです。

    VB.NET 2003  では正しい結果が得ることができています。

    ところが実際には VB.2013 x64 で実行すると

       Dt(0).ITem(0)   =>    "System String[]" {string}
       Dt(0).ITem(1)   =>    System.DBNull : {}

    上記のように正しくありません。  なぜか配列のデータの型式名称がセットされているようです。

    これを治すべく ネットで調べてみたところ ItemArray を用いれば対応できるようなので実験してみました。

    _____________________________________________________________________________

    Public Class Form1

        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
            Dim DT As New DataTable

            Dim dr As DataRow = DT.NewRow()

            Dim ar1 As New ArrayList
            Dim stary As String() = {"123", "456"}


            For I As Integer = 0 To stary.Length - 1

                DT.Columns.Add()
            Next


            ar1.Add(stary)

            Dr = DT.NewRow()

            dr.ItemArray() = ar1(0)
          
            DT.Rows.Add(dr)

        End Sub
    End Class

    _____________________________________________________________________________________

    VB.Net2013  x64でコンパイル実行 (OS Windows8 64ビット)

       Dt(0).ITem(0)   =>    "123"
       Dt(0).ITem(1)   =>    "456"

    正しい結果になりました。

    質問1  なぜこのように違いがあるのでしょうか? かつ x64でエラーにならないのはなぜ

    質問2  ほかにもこのように 32ビットから X64でのコンパイル実行すると結果が違うことがあるのでしょうか?

    以上について ご教授ください。


    • 編集済み mynobNNN 2014年9月3日 2:57 コードの転記に誤り。  DT.Rows.Add(dr) ==> DT.Rows.Add(ar1(0))
    2014年9月3日 2:10

回答

  • コードの転記に誤り。   DT.Rows.Add(dr) ==> DT.Rows.Add(ar1(0))

    スレチガイで編集されていたのですね。(^^;

    直前に回答したように、2003 の Add(Object[]) オーバーロードは、
    2005 以降で Add(params Object[]) に変更されています。

    そして、ar1(0) の戻り値が、As Object() や As String() ではなく、
    As Object であることに注意してください。

    そのため上記のコードは、
     2003 + Strict On の場合: コンパイルエラー
     2003 + Strict Off の場合: DT.Rows.Add(DirectCast( ar1(0), Object()) )と解釈
    というコンパイル結果となります。

    しかし 2005 以降では ParamArray 化されたため、結果として上記が
     DT.Rows.Add(New Object() {ar1(0)})
    と解釈されます。
    そして上記のように書けば、2003 でも 2013 でも同様に、"System.String[]" が格納されることになります。

    • 回答としてマーク mynobNNN 2014年9月3日 7:06
    2014年9月3日 3:53

すべての返信

  • dr とか Dr とか変じゃないですか? コードをアップするなら一字一句間違いないものをアップしていただけませんか?

    2014年9月3日 2:24
  • VB.NETでは変数の大文字小文字を区別しないので、drでもDrでも正しくアクセスできるのでは? その他、間違いがあるという指摘でしたらすみません。
    2014年9月3日 2:29
  • 試してみました。

    当方の環境 (Windows7 x64 / VS2013 Update3 / .NET Framework 4.5.1) で下記コードを実行してみたところ、

       Dt(0).ITem(0) => "123"
       Dt(0).ITem(1) => "456"

    になることが確認できました。

    Public Class Form1
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            Dim DT As New DataTable
            Dim dr As DataRow = DT.NewRow()
    
            Dim ar1 As New ArrayList
            Dim stary As String() = {"123", "456"}
            For I As Integer = 0 To stary.Length - 1
                DT.Columns.Add()
            Next
    
            ar1.Add(stary)
            dr = DT.NewRow()
            dr.ItemArray() = ar1(0)
            DT.Rows.Add(dr)
    
            Debug.Print(DT(0).Item(0))
            Debug.Print(DT(0).Item(1))
        End Sub
    End Class

    Windows8.1 /VS PRO 14 CTP(Visual Studio 次期バージョン) でも同じ結果になるので環境に起因する問題ではないと思われます。何かケアレスミス等されてませんか?


    MSDNフォーラムのヘルプは以下ご覧ください http://social.technet.microsoft.com/wiki/contents/articles/7359.forums-help-faq.aspx



    2014年9月3日 2:35
    モデレータ
  • > VB.NETでは変数の大文字小文字を区別しないので、drでもDrでも正しくアクセスできるのでは?

    実は「VB.NETでは変数の大文字小文字を区別しない」というのは知らなかったのですが・・・

    最初の方のコードで dr の定義はないようです。そこで自分の頭がコンパイルエラーになって思考停止してしまいました。

    2014年9月3日 2:47
  • > 当方の環境 (Windows7 x64 / VS2013 Update3 / .NET Framework 4.5.1) で下記コードを実行してみたところ、

    それは質問者さんによると「正しい結果になりました」という後者のコードではないですか?

    2014年9月3日 2:49
  • >以下のコードで VB.NET 2013で エラーにはなりませんが 求められる結果が正しくありません。

    と書かれていますが、そのコードって本当に動いていますか?? パッと見た目、DT.Rows.Add(dr)のdrがどこにもないように思いますが・・・

    #(追記)書いている間に、SurferOnWwwさんが同じことを指摘されましたね。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2014年9月3日 2:51
    モデレータ
  • すみません 転記ミスがありましたので 訂正しました

    ほかの方々も、お返事ありがとう御座います。

    訂正しました。

    • 編集済み mynobNNN 2014年9月3日 3:00
    2014年9月3日 2:58
  • お返事ありがとう御座います。

     前記での差異のことについてです。

    後記のItemarray はうまくゆきました。

    前記部分に 記載のミスがありましたので訂正しましました。

    2014年9月3日 3:02
  • 想像ですが、DataTable にスキーマの定義がされてないというのがそもそもの問題なのではないでしょうか? また、DT.Rows.Add(ar1(0)) の Add メソッドの引数は正しいのでしょうか?

    想定外のことなので結果は未定義で、環境によって何故結果が異なるかを議論しても意味のないことなのかも。

    【追伸】

    最初の質問のコードを変更されたようですが、そういうことをされると、最初の質問に対するレスが意味不明になってしまいます。現在のこのスレッドの参加者は分かっているからいいのですが、後で検索などでここにたどり着いた一般閲覧者には意味が分かりません。ここは質問者さん専用の Q&A サイトではなく、技術者・開発者同士の情報交換の場所と考えていただけると幸いです。

    • 編集済み SurferOnWww 2014年9月3日 3:23 追伸追加
    2014年9月3日 3:05
  • それは質問者さんによると「正しい結果になりました」という後者のコードではないですか?

    いつの間にか前者のコードが変わってますねw

    後から読む方が混乱するので、今後は最初の質問を編集せず、返信の方に修正コードを書いて頂くようお願いしたいです。


    MSDNフォーラムのヘルプは以下ご覧ください http://social.technet.microsoft.com/wiki/contents/articles/7359.forums-help-faq.aspx


    2014年9月3日 3:27
    モデレータ
  • 最初に提示されたコードは、コンパイルが通らないようですね。

    「配列オブジェクト」というのは、「String()」な Array のことでしょうか。
    それとも、ArrayList のことでしょうか。

    前者だとすれば、最初のコードの最後の行は、
    「DT.Rows.Add(ar)」ではなく、「DT.Rows.Add(ar1.ToArray())」でしょう。
    この場合は 2003 でも 2013 でも「"System.String[]", DBNull」が登録されます。

    後者だとすれば、最初のコードの最後の行は、
    「DT.Rows.Add(ar)」ではなく、「DT.Rows.Add(ar1)」でしょう。
    この場合は 2005 以降なら「"System.Collections.ArrayList", DBNull」が登録されます。
    2003 は文法エラーです。ただし、「DT.Rows.Add( New Object() { ar1 } )」とすれば、
    2003 でも 2013 でも「"System.Collections.ArrayList", DBNull」が登録されます。


    元のコードが分からないので、どこで悩んでいるのか掴みきれていませんが、
    おそらくは今回の件、x86 / x64 の違いでは無いと思います。
    そもそも  Rows.Add のオーバーロードは、.NET バージョンによって差異があるのです。

    1.1 当時は、「Sub Add(DataRow)」と「Sub Add(Object())」でしたが、
    2.0 にて「Sub Add(DataRow)」「Sub Add(ParamArray Object())」になっています。

    これにより、.Rows.Add に一次元配列を渡した場合の動作が変更されています。


    Dim stary1 As String() = {"101", "102"}
    Dim stary2 As Object() = {"201", "202"}
    Dim stary3 As New ArrayList(New Object() {New String() {"301", "302"}})
    
    DT.Rows.Add(stary1)
    DT.Rows.Add(stary2)
    DT.Rows.Add(New Object() { stary3 })
    DT.Rows.Add(stary3.ToArray())
    DT.Rows.Add(stary3)
    


    2.0 環境で上記を実行した場合、その結果は
     "101","102"
     "201","202"
     "System.Collections.ArrayList", DBNull
     "System.String[]", DBNull
     "System.Collections.ArrayList", DBNull
    となります。

    1.x 環境の場合は、
     "101","102"
     "201","202"
     "System.Collections.ArrayList", DBNull
     "System.String[]", DBNull
     '「DT.Rows.Add(stary3)」はコンパイルエラー BC30518
    です。

    2014年9月3日 3:39
  • コードの転記に誤り。   DT.Rows.Add(dr) ==> DT.Rows.Add(ar1(0))

    スレチガイで編集されていたのですね。(^^;

    直前に回答したように、2003 の Add(Object[]) オーバーロードは、
    2005 以降で Add(params Object[]) に変更されています。

    そして、ar1(0) の戻り値が、As Object() や As String() ではなく、
    As Object であることに注意してください。

    そのため上記のコードは、
     2003 + Strict On の場合: コンパイルエラー
     2003 + Strict Off の場合: DT.Rows.Add(DirectCast( ar1(0), Object()) )と解釈
    というコンパイル結果となります。

    しかし 2005 以降では ParamArray 化されたため、結果として上記が
     DT.Rows.Add(New Object() {ar1(0)})
    と解釈されます。
    そして上記のように書けば、2003 でも 2013 でも同様に、"System.String[]" が格納されることになります。

    • 回答としてマーク mynobNNN 2014年9月3日 7:06
    2014年9月3日 3:53
  • 訂正したことが混乱を招いたようで 大変ご迷惑をおかけしました。

    なるべくわかりやすくがモットー ということで ご指摘の通り投稿後のことも考えて 訂正するようにしますので

    今後ともよろしくお願いいたします。

    2014年9月3日 7:05
  • 長文にて詳細にご説明いただき 感謝します。

    よくわかりました。 コンパイルオプションについて Strict ON になっていないことが

    原因のように思えます。 データの型を下記のように書けば問題は発生しないものと思えます。

    x64 環境に移行するにあたり 下記のようにしたいと思います。

    Dr = DT.newrow

    Dr.ItemArray =   Ctype(AR1(0),String())

    DT.ROWS.add(dr)

    もしくは

    __________________________________________  

    DR = dt.newrow

      Dim C as integer=0

      For St as string in Ctype(AR1(0),String())

           dr.item(c) = st

           C += 1

      Next

      DT.Rows.add(dr)

    ____________________________________

    のようにすれば 大丈夫のように思えます。

    ほかにも問題箇所はあるかもしれませんが・・・・・

    どうもありがとうございました。

    2014年9月3日 7:20
  • 長文にて詳細にご説明いただき 感謝します。

    長文で済みません…。

      For St as string in Ctype(AR1(0),String())

    そもそも 2005 以降では、ArrayList が必要になることは稀です。

    Object 型でしか保持できない ArrayList ではなく、
    型を明確にできる List(Of String) などで管理するようにすれば、
    こうした型変換の手間からも逃れられるかと思いますよ。

    2014年9月3日 7:28
  • そもそも 2005 以降では、ArrayList が必要になることは稀です。

    Object 型でしか保持できない ArrayList ではなく、
    型を明確にできる List(Of String) などで管理するようにすれば、
    こうした型変換の手間からも逃れられるかと思いますよ。

    私も魔界の仮面弁士さんと同意見です。ジェネリックとArrayList の違いについては、以下の記事をご覧ください。

    ジェネリック・クラスで変わるC#とVBのコレクション

    Visual Basic におけるジェネリック型 (Visual Basic)

    ジェネリックの利点 (C# プログラミング ガイド)


    MSDNフォーラムのヘルプは以下ご覧ください http://social.technet.microsoft.com/wiki/contents/articles/7359.forums-help-faq.aspx

    2014年9月3日 8:05
    モデレータ
  • 個人的には VB.NET は止めて C# を使いましょうと言いたいのですが、そうも行かない事情があるのでしょうね。

    > コンパイルオプションについて Strict ON になっていないことが原因のように思えます。

    そうではない(それは根本的な問題ではない)と思いますけど。

    Option Strict On にすると、今までの VB による勝手な型変換を、コンパイル時にエラーにしてバグの混入を防ぐという点ではメリットがあると思います。

    > データの型を下記のように書けば問題は発生しないものと思えます。

    しかし、Object 型の代入を許すようなコードを書いては元も子もないのでは?

    先のレスで書きましたように、DataTable にスキーマの定義がされてないというのがそもそもの問題だと思います。

    以下のページのサンプルコードのようにすれば、今回のような問題は起こりえないはずです。

    DataColumn クラス
    http://msdn.microsoft.com/ja-jp/library/system.data.datacolumn(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2


    > x64 環境に移行するにあたり 下記のようにしたいと思います。

    32-bit / 64-bit の話は今回の問題とは何の関係もないです。

    それとは別の問題があって、x64 や AnyCPU でコンパイルすると場合によっては動かなくなるということは認識されてますか?

    詳しくは以下のページを見てください。

    Part 2. .NET Framework 2.0 アプリケーションの 64 ビット対応
    http://blogs.msdn.com/b/nakama/archive/2008/11/06/part-2-net-framework-2-0-64.aspx

    もし、このあたりに詳しくなければ Part 1 から読むことをお勧めします。

    Part 1. 64 ビット Windows OS の基本知識
    http://blogs.msdn.com/b/nakama/archive/2008/10/30/part-1-64-windows-os.aspx

    2014年9月3日 10:23