- unibon
- ぬし
- 会議室デビュー日: 2002/08/22
- 投稿数: 1532
- お住まい・勤務地: 美人谷 良回答(20pt)
|
投稿日時: 2005-12-26 14:58
いまだにちょっとしたプログラムには VB(バージョン 5 や 6 あたり)を使うことが多いです。はやく VB からおさらばしたいと思っているのですが、まだまだ逃れることができません。Excel がマクロに VBA を使っていることも一因かもしれません(Excel も良く利用するので)。
VB や VBA でも、一応、クラスもあるし、オブジェクトの扱いも古い言語の割には、非常に今風に使えてあなどれません。多くのことは Java や C# と似たようなロジックで書けます。しかし、配列がどうしてもうまく扱えません。
前置きが長くなりましたが、
とくに、要素数 0 の配列ができないのが非常に苦しいのです。
(昔の C で malloc(0) が保証されていなかったのと同じような雰囲気です。)
解決法としては、つぎのものが思いつきます。
(1) Array を使う( x = Array() みたいに)
これだと型が使えない。
(2) Collection を使う( Set x = new Collection みたいに)
これも型が使えない。インデックスが1から始まるのもなんか使いにくい。
(3) 普通の配列を使うが、常にインデックス 0 を使わない。
うっかりインデックス0にアクセスしてハマりそう。
(4) すっごい裏ワザ
なにかないですか?
みなさんはどう対処されているのでしょうか?
|
- じゃんぬねっと
- ぬし
- 会議室デビュー日: 2004/12/22
- 投稿数: 7811
- お住まい・勤務地: 愛知県名古屋市
|
投稿日時: 2005-12-26 15:10
引用: |
|
unibonさんの書き込み (2005-12-26 14:58) より:
みなさんはどう対処されているのでしょうか?
|
VB で困るのが、あるメソッドの戻り値が配列だった場合に、
Null (Nothing) で返すことができないことっすね。
要するに、
コード: |
|
Dim stHoges() As String
stHoges = GetHogeArrays() '配列を返す
If stHoges Is Nothing Then
Exit Function
End If
|
といった評価ができない。(実行時エラーになる)
UBound を使っても 0 という値で返らないですし... (実行時エラーになる)
ですので、Is Nothing の代わりに、SafeArrayGetDim 関数と、
GetMem4 関数を使ったメソッドを書いてます。
これのためにわざわざ、クラス モジュールを書くのも...
_________________ C# と VB.NET の入門サイト
じゃんぬねっと日誌
|
- でにす
- 会議室デビュー日: 2005/12/15
- 投稿数: 4
|
投稿日時: 2005-12-26 15:15
えっ?
関数の引数をVariantにするんじゃダメなの?
Nothingも戻せると思うけど?
|
- じゃんぬねっと
- ぬし
- 会議室デビュー日: 2004/12/22
- 投稿数: 7811
- お住まい・勤務地: 愛知県名古屋市
|
投稿日時: 2005-12-26 15:25
引用: |
|
でにすさんの書き込み (2005-12-26 15:15) より:
関数の引数をVariantにするんじゃダメなの?
Nothingも戻せると思うけど?
|
たとえば、String() だと型が一致しないという実行時エラーになると思いますが...
# もしかして、最初の質問を読み違えてますか? > わたし
_________________ C# と VB.NET の入門サイト
じゃんぬねっと日誌
|
- 未記入
- ぬし
- 会議室デビュー日: 2004/09/17
- 投稿数: 667
|
投稿日時: 2005-12-26 15:34
Split 関数が要素数 0 の配列を返すらしい。
コード: |
|
a = Split("", "/")
Debug.Print "aの要素数 " & UBound(a) - LBound(a) + 1
|
aの要素数 0 となります。LBound(a) = 0, UBound(a) = -1 となるので、LBound から UBound までの For ループで使ってもループ内に制御が移らない。使い方によっては役に立つとか立たないとか。
|
- じゃんぬねっと
- ぬし
- 会議室デビュー日: 2004/12/22
- 投稿数: 7811
- お住まい・勤務地: 愛知県名古屋市
|
投稿日時: 2005-12-26 15:57
コード: |
|
'/* MyStrings モジュール */
Option Explicit
Private Declare Sub GetMem4 Lib "MSVBVM60.DLL" ( _
ByRef aParam() As Any, _
ByRef lCount As Long _
)
'/ Is Nothing の代わり
Public Function IsNothing(ByRef stArgs() As String) As Boolean
Dim lCount As Long
Call GetMem4(stArgs, lCount)
If lCount = 0 Then
IsNothing = True
Else
IsNothing = False
End If
End Function
'/ 未割り当ての状態を取得 (メソッド内は空でもいい)
Public Function GetNothing() As String()
Dim stNullable() As String
GetNothing = stNullable
End Function
'/ 配列 0 の状態を取得
Public Function GetEmpty() As String()
GetEmpty = Split(vbNullString, vbNullChar)
End Function
|
とりあえず、実験結果。
コード: |
|
'/* 実験用の Form */
Option Explicit
Private Sub Command1_Click()
'/ 今回のターゲット
Dim stHoges() As String
'/ 未割り当てなので Null
If MyStrings.IsNothing(stHoges()) = True Then
Call MsgBox("Null")
Else
Call MsgBox("Not Null")
End If
'/ 要素を 1 つ割り当てる
ReDim stHoges(0)
'/ 割り当ててるので Not Null
If MyStrings.IsNothing(stHoges()) = True Then
Call MsgBox("Null")
Else
Call MsgBox("Not Null")
End If
'/ 未割り当てにする
stHoges = MyStrings.GetNothing()
'/ 未割り当てなので Null
If MyStrings.IsNothing(stHoges()) = True Then
Call MsgBox("Null!")
Else
Call MsgBox("Not Null!")
End If
'/ 実行時エラーになる
'Call MsgBox(CStr(UBound(stHoges)))
'/ 要素数 0 の配列にしてみる
stHoges = MyStrings.GetEmpty()
'/ 0 となる
Call MsgBox(CStr(LBound(stHoges)))
'/ -1 となる
Call MsgBox(CStr(UBound(stHoges)))
End Sub
|
うーん、厳密には Null (Nothing) という文言じゃダメなんでしょうけど。
_________________ C# と VB.NET の入門サイト
じゃんぬねっと日誌
|
- unibon
- ぬし
- 会議室デビュー日: 2002/08/22
- 投稿数: 1532
- お住まい・勤務地: 美人谷 良回答(20pt)
|
投稿日時: 2005-12-26 23:12
みなさまありがとうございます。
質問を補足すると、たとえば Java でいうと、こんな、
コード: |
|
public class Hoge {
public static int[] foo(int x) {
if (x < 0) {
return null;
} else if (x == 0) {
return new int[0];
} else if (x > 0) {
return new int[x];
} else {
throw new IllegalStateException();
}
}
public static void bar(int[] x) {
for (int i = 0; i < x.length; i++) {
System.out.println("i = " + i + ", value = " + x[i]);
}
}
public static void main(String[] args) {
int[] a = Hoge.foo(-1); // null
if (a != null) {
bar(a);
}
int[] b = Hoge.foo(0); // int[0]
if (b != null) {
bar(b);
}
int[] c = Hoge.foo(1); // int[1]
if (c != null) {
bar(c);
}
}
}
| 感じのことを、VB でもやりたいのです。
すなわち、上記でいうところの変数 b の状態を実現したいです。
要素数 0 を特別扱いせずに使いたいのです。
b と c を区別せずに扱いたいのです。
また、ついでに a の状態もやれるものなら実現したいです。
こっちは VB でも変数の型を Variant にして Null を使えばできますが、型がなくなってしまうので...
引用: |
|
未記入さんの書き込み (2005-12-26 15:34) より:
Split 関数が要素数 0 の配列を返すらしい。
|
Split を試そうとしたのですが、これは VB6 以上から備わっているみたいですね。あと、これは String 専用になってしまうという捉え方であっているでしょうか。
引用: |
|
未記入さんの書き込み (2005-12-26 15:34) より:
コード: |
|
a = Split("", "/")
Debug.Print "aの要素数 " & UBound(a) - LBound(a) + 1
|
aの要素数 0 となります。LBound(a) = 0, UBound(a) = -1 となるので、LBound から UBound までの For ループで使ってもループ内に制御が移らない。使い方によっては役に立つとか立たないとか。
| そうです。もくろみはこれです。要素数 0 でも LBound や UBound をシームレスに使いたいわけです。
引用: |
|
じゃんぬねっとさんの書き込み (2005-12-26 15:10) より:
ですので、Is Nothing の代わりに、SafeArrayGetDim 関数と、
GetMem4 関数を使ったメソッドを書いてます。
|
SafeArrayGetDim や GetMem4 などの API を使うとできるのでしょうか。このあたりをちょっと調べてみます。
#しかし GetMem4 に至っては Google で検索しても日本語だと1件しかヒットしない。
|
- じゃんぬねっと
- ぬし
- 会議室デビュー日: 2004/12/22
- 投稿数: 7811
- お住まい・勤務地: 愛知県名古屋市
|
投稿日時: 2005-12-26 23:25
引用: |
|
unibonさんの書き込み (2005-12-26 23:12) より:
SafeArrayGetDim や GetMem4 などの API を使うとできるのでしょうか。このあたりをちょっと調べてみます。
|
一応使用例のコードは提示していますよ。
ただ、見て頂くと察しが付くと思いますが、これも、どの VB でも使えるというわけでは...
# 基本的には 0 だけ (要素数:1) の場合を null と扱ったりするしか逃げ道がないです。
# あと、エラー処理という方法もありますが、これは生理的に受け付けません。。。
_________________ C# と VB.NET の入門サイト
じゃんぬねっと日誌
|