-
「VB.NETの値型と参照型について」の記事で「参照型」には「配列」があると触れましたが、 今回はそのことを詳しく見ていきます。
まずは配列の宣言方法ですが、以下の様に定義できます。
Dim 配列名(指標上限値) As 各要素のデータ型
配列宣言の例としては以下の様になります。
3,4行目の配列の初期化を含む宣言の場合、「指標上限値」は省略する必要があります。
Dim intArr(4) As Integer Dim lngArr(5) As Long, dblArr(10) As Double, strArr(20) As String Dim nArr() As Integer = {1, 2, 3, 4, 5} '{}で括り初期値リストを記述(Integer型) Dim sArr() As String = {"AAA", "BBB", "CC"} '{}で括り初期値リストを記述(String型)
通常配列を扱う場合は、配列の添え字を指定して各要素のデータにアクセスします。 例えば以下の様な感じです。'配列宣言 Dim intArr(4) As Integer '各要素に適当な値を入れる intArr(0) = 1 intArr(3) = 2 '指標=3の要素を表示する Console.WriteLine(String.Format("intArr(3) = {0}", intArr(3))) 'intArrの要素を各指標値に設定する For i As Integer = LBound(intArr) To UBound(intArr) intArr(i) = i Next
「LBound」「UBound」は配列の指標の最小値及び最大値を返す関数です。 配列の全てを処理する場合、この関数を使うほうがバグが出にくいと思います。
そこで参照型であることを説明するために以下のソースを見てください。
'配列宣言(指標MAX指定) Dim intArr(4) As Integer 'intArrの要素を各指標値に設定する For i As Integer = LBound(intArr) To UBound(intArr) intArr(i) = i Next '配列宣言(指標指定無し) Dim intArr2() As Integer '配列の参照の枠のみ intArr2 = intArr '配列の参照コピー For i As Integer = LBound(intArr2) To UBound(intArr2) Console.WriteLine(String.Format("intArr2(" & i & ") = {0}", intArr2(i))) Console.WriteLine(String.Format("intArr (" & i & ") = {0}", intArr(i))) Next
このソースで表示される結果からわかるように、 「intArr2」と「intArr」が示すデータは全く同じものになります。 「intArr2」に「intArr」をコピーしているので当然なのですが、 「intArr」の配列の実体は5個のInteger型のデータの並びなのですが、 それを参照しているものが「intArr」の変数の中身なのです。 図で示すと以下の様な感じです。
更に以下のソースを見てください。 「intArr」を宣言後、要素にデータを設定し、その後で「intArr」に「Nothing」を入れています。
'配列宣言(指標MAX指定) Dim intArr(4) As Integer 'intArrの要素を各指標値に設定する For i As Integer = LBound(intArr) To UBound(intArr) intArr(i) = i Next '参照をクリアする intArr = Nothing 'エラーが発生する部分 Console.WriteLine(String.Format("intArr(0) = {0}", intArr(0)))
これを実行すると最後のコンソール出力の行でエラーが発生します。 「intArr」が指し示していた内容がクリアされたため、 最初に宣言したときの配列の実体への参照が出来なくなっているからです。
このことから、配列の変数も、クラス変数と同様な感じが分かると思います。
=====
2016/01/30:の時の情報
PR -
VB.NETで変数として宣言できるデータ型は大きく分けて「値型」と「参照型」に分けられます。 「参照型」は、初めてVB.NETを使い始めた方にとっては、なかなか理解しにくい部分でもあります。 かく言う私もそのひとりではありますが。
「値型」は変数としてよく使う、Integer、Double、Decimal、DateTime及び構造体、列挙型などで、 「参照型」はクラス、配列などです。
「値型」のInteger、Doubleなどは直感的に分かりやすいのですが、 「参照型」の実際はどうなのか理解しにくいものです。
「値型」とは変数を保持しているメモリ上に直接データをアクセスできるもので、 「参照型」とはその変数に保持しているのは、参照型データの実体を指し示すポインタ的なものです。
言葉で説明すると何のことか分かりにくいので 「値型」と「参照型」を比較する為、構造体とクラスで説明します。
以下の様な構造体とクラスを宣言します。
'テスト用構造体宣言 Structure StrucTest Public nTest As Integer 'Publicなメンバ変数 End Structure 'テスト用クラス宣言 Class ClassTest Public nTest As Integer 'Publicなメンバ変数 End Class
このテスト用構造体を以下の様に使います。 このソースを適当なところで入力し、デバッグモードで1行ずつ実行させると分かりやすいです。
'構造体のテスト Dim ST1 As StrucTest '構造体「ST1」の宣言 ST1.nTest = 1 'メンバ変数「nTest」に1を設定 Dim ST2 As StrucTest '構造体「ST2」の宣言 ST2 = ST1 '「ST1」の内容を「ST2」にコピー ST2.nTest = 2 'メンバ変数「nTest」に2を設定 'クラスのテスト Dim CT1 As ClassTest 'クラス「CT1」の宣言 CT1 = New ClassTest 'クラスの実体を生成し「CT1」に代入 CT1.nTest = 1 'メンバ変数「nTest」に1を設定 Dim CT2 As ClassTest 'クラス「ST2」の宣言 CT2 = CT1 '「CT1」の内容を「CT2」にコピー CT2.nTest = 2 'メンバ変数「nTest」に2を設定
構造体のテストでは、1行目で構造体「ST1」の宣言をしていますが、この時点で「ST1」の実体がメモリに確保されます。 宣言の後で直接「ST1」のメンバ変数「nTest」に1を設定します。
その後で構造体「ST2」の宣言し、「ST2」に「ST1」を代入していますが、 「ST2」に割り当てられた実体に「ST1」の全てがまるまるコピーされます。
「ST2」と「ST1」の実体は全く別の独立したメモリ領域に存在していますので、 「ST2」のメンバ変数「nTest」に2を設定しても、「ST1.nTest」および「ST2.nTest」は異なる値を保持しています。
クラスのテストでは、1行目でクラス「CT1」の宣言をしていますが、この時点ではCT1には実体は設定しておらず、 値的には「Nothing」になっています。尚、値が「Nothing」である参照型の変数のメソッドやプロパティにアクセスしても エラーが発生します。
その後の「New」を行うことで、「ClassTest」の実体がメモリに確保され、その参照が「CT1」に代入されます。 参照とはメモリに確保された「ClassTest」の入れ物を指し示すポインタの様なものです。
この時点で「CT1」のメンバ変数にアクセスが可能になります。
クラスのテストでは、4行目でクラス「CT2」の宣言を行いますが、当然「CT2」の中身は「Nothing」のままです。 ここで5行目で「CT2」に「CT1」の値をコピーしています。 値のコピーと言っても、中身は「ClassTest」の実体メモリへの参照する値です。
(「ClassTest」の入れ物を指し示すポインタのコピー)
最後の行で「CT2」を使って「CT2.nTest」へ2を設定しています。 ここで「CT1」の値をデバッガ等でみますと「2」になっているはずです。 参照型を初めて見る方にとっては、おやと思うでしょうが、 「CT2」も「CT1」もメモリ上の同じ場所を指し示しているので、 「CT1.nTest」で見ても「CT2.nTest」で見ても同じ値になります。
尚、クラス宣言と実体生成を以下の様に記述できます。 「CT2」もNewで生成すれば、全く別のクラス領域を指し示すことになりますので、 「CT1.nTest」と「CT2.nTest」は別の値になります。
'クラスのテスト2 Dim CT1 As New ClassTest 'クラス「CT1」の実体を生成し「CT1」に代入 CT1.nTest = 1 'メンバ変数「nTest」に1を設定 Dim CT2 As New ClassTest 'クラス「CT2」の実体を生成し「CT2」に代入 CT2.nTest = 2 'メンバ変数「nTest」に2を設定
=====
2016/01/28:の時の情報
-
プログラミングにおいて文字列型「String」はいろんな場面で使います。 名前を表示する時に使ったり、数値を文字列型で退避したりと枚挙に暇がありません。
この「String」ですがよく使う数値型のInteger型やDouble型、Decimal型などとは性格が異なり内部的には「クラス」の扱いなのです。 「クラス」は変数を宣言してもその変数を初期化(インスタンス化)しないと、その「クラス」のプロパティやメソッドが使えません。 使えないというか、インスタンス化されていない「クラス」のプロパティにアクセスするとその時点でエラーが発生します。 以下はエラーの発生するソースの例です。
Dim str As String Dim nLen As Integer nLen = str.Length 'エラーが発生します
変数 str を初期化せずにString型のLengthプロパティを使うとエラーが発生します。 「NullReferenceException」エラーが発生します。(NULLであるオブジェクトを参照したというエラー)
このエラーを回避するには、文字列型の宣言するところで初期化(空文字を代入する)を行ってから、プロパティやメソッドを使います。 また、文字列長のみ知りたいのであれば、標準で備わっている「Len」関数を使います。
Dim str As String Dim nLen As Integer nLen = Len(str) 'Microsoft.VisualBasic.Stringsモジュール内関数 Dim str2 As String = "" '文字列の初期化 nLen = str2.Length 'エラーは発生しません
Microsoft.VisualBasic.Stringsモジュールには昔のBasicを引きずった関数がいろいろありますが、 その関数を使うか、文字列のクラスのメソッドを使うかは好みの問題かと思います。 今後は「String」クラスの「Substring」メソッドを使うのが主流になるとは思います。
但し、「Left」「Right」「Mid」の関数は昔から便利だったので結構使いたくなります。 これらの関数の「String」クラスの「Substring」メソッドでの方法を以下に記します。
Dim str As String = "0123456789" Dim str2 As String = "" 'Microsoft.VisualBasicモジュール関数 str2 = Microsoft.VisualBasic.Left(str, 2) '"01" str2 = Microsoft.VisualBasic.Right(str, 2) '"89" str2 = Microsoft.VisualBasic.Mid(str, 2, 3) '"123" 'Substringメソッド使用 str2 = str.Substring(0, 2) 'Left(str, 2) str2 = str.Substring(str.Length - 2, 2) 'Right(str, 2) str2 = str.Substring(1, 3) 'Mid(str, 2, 3)
=====
2016/01/28:の時の情報
-
前回はCOMオブジェクトを使ってエクセルファイルのアクセスを行いましたが、今回はOleDBを使います。
COMオブジェクトだとプロセスがメモリに残ることを気にしないといけませんが、OleDBでは気にしなくても良いようです。
以下のソースでは引数としてエクセルファイルのファイル名を指定しています。
関数の先頭で、関数内で使用するOleDB接続クラス、OleDBデータアダプタクラス、データテーブルクラスの宣言を行っています。 最初にOleDB接続クラスを使ってエクセルとの接続を行います。 ファイルの拡張子が「xlsx」と「xls」では接続されるOleDBが異なります。 「xls」では「Microsoft.Jet.OLEDB.4.0」だったのですが、「xlsx」では「Microsoft.Ace.OLEDB.12.0」になります。
次に、OleDBデータアダプタクラスを使ってエクセルシートのデータにアクセスします。 エクセルのシートをデータベースのテーブルの様に扱えます。 SQL文の中のFROM句で「Sheet1$」と記述している部分がそうです。 抽出範囲を指定する場合にはソースの様に「Sheet1$A1:C10」とすれば、 A,B,Cカラムの1行目から10行目までの指定になります。
データアダプタを作成後、データアダプタのメソッド「Fill」でデータテーブル、 エクセルのシートからデータを全て取り込みます。 後は、データテーブルから1行ずつデータ行を取得しデータ処理を行います。
以下のソースはテストですので単にデータを文字列として連結して表示しています。
関数の終了処理では使用したオブジェクトの解放を行います。
''' <summary>エクセルファイル処理</summary> ''' <param name="strXlsxFileName">xlsxファイル名</param> Private Sub TestXlsx2(ByVal strXlsxFileName As String) 'OleDB接続クラス Dim oleCon As System.Data.OleDb.OleDbConnection = Nothing 'OleDBデータアダプタクラス Dim oleAdp As System.Data.OleDb.OleDbDataAdapter = Nothing 'データテーブルクラス Dim adoTbl As System.Data.DataTable = Nothing Try 'EXCELとの接続 Try oleCon = New System.Data.OleDb.OleDbConnection() If System.IO.Path.GetExtension(strXlsxFileName).ToLower = "xlsx" Then oleCon.ConnectionString = "Provider=Microsoft.Ace.OLEDB.12.0;Data Source=" & _ System.IO.Path.GetFullPath(strXlsxFileName) & ";Extended Properties=Excel 12.0;" Else oleCon.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & _ System.IO.Path.GetFullPath(strXlsxFileName) & ";Extended Properties=Excel 8.0;" End If oleCon.Open() Catch ex As Exception MsgBox("...EXCELオープン失敗") Exit Sub End Try 'EXCELデータを全て取得(仮にA,B,Cカラムの10行分) Dim strSQL As String = "SELECT * FROM [Sheet1$A1:C10]" Try oleAdp = New System.Data.OleDb.OleDbDataAdapter(strSQL, oleCon) adoTbl = New System.Data.DataTable oleAdp.Fill(adoTbl) Catch ex As Exception MsgBox("...EXCELデータ取得失敗") Exit Sub End Try '文字列の連結 Dim strTest As String = "" Dim adoRow As System.Data.DataRow For Each adoRow In adoTbl.Rows strTest &= adoRow(0).ToString.Trim strTest &= adoRow(1).ToString.Trim strTest &= adoRow(2).ToString.Trim Next MsgBox(strTest) Finally If Not adoTbl Is Nothing Then adoTbl.Clear() adoTbl.Dispose() End If adoTbl = Nothing If Not oleAdp Is Nothing Then oleAdp.Dispose() End If oleAdp = Nothing If Not oleCon Is Nothing Then oleCon.Close() oleCon.Dispose() End If oleCon = Nothing End Try End Sub
尚、今回の処理で以下のエラーが出た場合はMicrosoftの再頒布可能コンポーネントをインストールして下さい。
「'Microsoft.Ace.OLEDB.12.0' プロバイダはローカルのコンピュータに登録されていません。」
■Microsoftのリンク先 Microsoft Access データベース エンジン 2010 再頒布可能コンポーネント
=====
2016/01/27:の時の情報
-
「CSE」を使ってPostgreSQLのデータベースのテーブルに、CSVデータ入力(インポート)を行ってみます。
まず最初に、前回のCSVデータ出力で出力された「dt_data1.csv」ファイルをCSEで入力してみます。 「DBエクスプローラ」に表示されているテーブル「dt_data1」の上で右クリックします。 メニューの中から、真ん中下あたりの「データをインポート」を選びます。
「データをインポート」を選択した後に以下の図の様に、「インポートするファイルを指定して下さい」ダイアログが表示されます。 「dt_data1.csv」を選択し「開く」ボタンをクリックします。
インポート処理のメッセージがコンソールに表示されます。 「dt_data1.csv」のデータはテーブルの中に既に存在しますので、3行のエラーが表示されます。 当然インポート処理は失敗となります。
そこで、新しいCSVファイルをテキストエディタで作成してみます。 「dt_data1-inp1.csv」として以下の図の様に3件のデータを作成します。
再度「DBエクスプローラ」に表示されているテーブル「dt_data1」の上で右クリックし、「データをインポート」を選択します。 今回は「dt_data1-inp1.csv」のファイルを選択します。
インポート処理の様子が下図の様に、コンソールウインドウに表示されます。
インポートされたか確認する為、「DBエクスプローラ」に表示されているテーブル「dt_data1」の上で右クリックし、 「全データを開く」を選択します。
これで、CSEでのCSVファイルのインポートの説明を終わりますが、 上の例でも示しましたが、インポートするCSVファイルのデータが、テーブルに既にあるデータと キーが重複する場合は注意が必要です。
=====
2015/03/23:の時の情報