C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[3]: クラスが作れません。


(過去ログ 173 を表示中)

[トピック内 8 記事 (1 - 8 表示)]  << 0 >>

■99806 / inTopicNo.1)  クラスが作れません。
  
□投稿者/ 白音 (4回)-(2022/06/07(Tue) 09:30:17)

分類:[.NET 全般] 

エクセルのVBAでクラスモジュールを作ろうとしたのですが
「クラスモジュール 定数、固定長文字列、配列、
ユーザー定義型およびDeclare ステートメントは
オブジェクトモジュールのパブリックメンバーとしては使用できません」
となります。

[クラスモジュール]
└[clsNameData]

--------------------------------------------------
Option Explicit

Public name As String
Public data(10) As Double

Private Sub Class_Initialize()
Dim dtAs Variant
name = ""
For Each mnt In data
dt= 0
Next
End Sub
--------------------------------------------------

試行錯誤の結果以下のようにしました。
これでいいのでしょうか?

--------------------------------------------------
Option Explicit

Public name As String
Public data As Variant

Private Sub Class_Initialize()
Dim dtAs Variant
name = ""
  ReDim data(10)
For Each mnt In data
dt= 0
Next
End Sub
--------------------------------------------------

引用返信 編集キー/
■99807 / inTopicNo.2)  Re[1]: クラスが作れません。
□投稿者/ 魔界の仮面弁士 (3386回)-(2022/06/07(Tue) 10:14:08)
No99806 (白音 さん) に返信
> Private Sub Class_Initialize()
>  Dim dtAs Variant
>  name = ""
>  For Each mnt In data
>   dt= 0
>  Next
> End Sub

dtAs → dt As という点はさておき、変数 mnt が未定義ですし、
そもそもループ処理が意味不明過ぎるんですが…。


> 「クラスモジュール 定数、固定長文字列、配列、
> ユーザー定義型およびDeclare ステートメントは
> オブジェクトモジュールのパブリックメンバーとしては使用できません」

Public メンバーにしなければ OK です。(Private なら構わない)

そもそも、モジュールレベルの配列を Public として公開した場合、
クラス以外の場所から ReDim や Erase されてしまう可能性があるので、
「カプセル化」の観点からはあまり望ましくなかったりします。


'--- 案1: 白音さん御自身が行われたように、「内部処理形式が Double の配列であるバリアント型」を使う



'--- 案2: 配列として公開するのではなく、添字引数付きのスカラー値プロパティの形で公開する
' → ActiveX 版 ListBox の List プロパティがこの形ですね。
Option Explicit

Public Name As String
Private m_data(10) As Double

Public Property Get Data(ByVal index As Integer) As Double
  Data = m_data(index)
End Property
Public Property Let Data(ByVal index As Integer, ByVal newValue As Double)
  m_data(index) = newValue
End Property


'--- 案3: 「配列を戻り値として返す Function」または「配列を返す読み取り専用プロパティ」として公開する
' ※プロパティにすると扱い辛いので、このケースでは Function の方が便利です。
'
Option Explicit

Public Name As String
Private m_data(10) As Double

Public Property Get Data() As Double()
  Data = m_data
End Property

'あまりお奨めはしないけど、書き込み可能にするならこんな感じ
'Public Property Let Data(ByRef newArray() As Double)
'  Erase m_data
'  Dim vnt As Variant, idx As Integer
'  idx = LBound(m_data, 1)
'  For Each vnt In newArray
'    m_data(idx) = vnt
'    idx = idx + 1
'  Next
'End Property
引用返信 編集キー/
■99808 / inTopicNo.3)  Re[2]: クラスが作れません。
□投稿者/ 白音 (6回)-(2022/06/07(Tue) 14:25:44)
No99807 (魔界の仮面弁士 さん) に返信
ありがとうございます。

> dtAs → dt As という点はさておき、変数 mnt が未定義ですし、
mnt は dt のことですね。

> そもそもループ処理が意味不明過ぎるんですが…。
data の中身を0で初期化するためです。

>
> '--- 案1: 白音さん御自身が行われたように、「内部処理形式が Double の配列であるバリアント型」を使う
>
data にデータを入れる方法がわかりません。

dim nd as clsNamedata
Set nd = New clsNamedata

nd.data(0) = 1

1 になりません。


>
>
> '--- 案2: 配列として公開するのではなく、添字引数付きのスカラー値プロパティの形で公開する
> ' → ActiveX 版 ListBox の List プロパティがこの形ですね。

この場合

dim nd as clsNamedata
Set nd = New clsNamedata

nd.data = Array(1,2,3,・・・)

を行うにはどうすればよいのでしょうか?

引用返信 編集キー/
■99809 / inTopicNo.4)  Re[2]: クラスが作れません。
□投稿者/ 白音 (7回)-(2022/06/07(Tue) 15:03:59)
No99807 (魔界の仮面弁士 さん) に返信

プロパティの使い方がわかりません。

Dim mdata As clsNameData
Set mdata = New clsNameData
call mdata.Data (0, 0)

「プロパティの使い方が不正です。」
となってしまいます。


引用返信 編集キー/
■99810 / inTopicNo.5)  Re[3]: クラスが作れません。
□投稿者/ 魔界の仮面弁士 (3387回)-(2022/06/07(Tue) 15:56:36)
No99808 (白音 さん) に返信
>>そもそもループ処理が意味不明過ぎるんですが…。
> data の中身を0で初期化するためです。

初期化も何も、Double の初期値は 0.0 ですよ。

なので、ループするまでも無く
 Private Sub Class_Initialize()
  Dim d(0 To 10) As Double
  Me.m_data = d
 End Sub
あるいは
 Private Sub Class_Initialize()
  ReDim m_data(0 To 10) As Double
 End Sub
で済む話です。


そもそも、「For Each dt In 数値の配列」のループでは
配列の中身を書き換えることはできません。使うならば
For Each〜Next ではなく、For〜Next を使うべきです。



コレクションを列挙して、個々の要素のプロパティを書き換えるような用途なら
For Each でも良いのですけれどね。



>>'--- 案1: 白音さん御自身が行われたように、「内部処理形式が Double の配列であるバリアント型」を使う
> data にデータを入れる方法がわかりません。
> nd.data(0) = 1
> 1 になりません。

他クラスのメンバーから得られるのは、元の変数への参照そのものではなく、値のコピーだからです。

※Module と違って、クラスだと配列を Public にできないのもそれが理由。

これがコピーであることは、たとえば呼び出し側で
 Debug.Print Hex(VarPtr(nd.data))
を行った結果と、クラス側で
 Debug.Print Hex(VarPtr(Me.data))
を呼び出した場合、得られるアドレスが異なることからも判断できます。


配列の一部だけを直接書き換えようとするのではなく、
配列全体のコピーをいったん変数に受け取ってから、
その変数の内容を書き換えた上で、
書き換えた新しい配列全体を、改めて渡す方法を採れます。
 
 'Dim dbl As Variant
 Dim dbl() As Double
 dbl = nd.data
 dbl(0) = 0.5
 nd.data = dbl


配列の一部だけを書き換えたい場合は、案2 のようにするか、
またはクラス側に「内容を書き換えるためのメソッド」を設けるようにします。



>>'--- 案2: 配列として公開するのではなく、添字引数付きのスカラー値プロパティの形で公開する
>>' → ActiveX 版 ListBox の List プロパティがこの形ですね。
> nd.data = Array(1,2,3,・・・)

このケースでは、左辺の data は添字引数が必須なプロパティになっています。
また、添字を指定した場合の戻り値は、As Double です。ですから Array の代入はできません。

たとえば、
 Dim dbl1() As Double
 Dim dbl2 As Double
に対して
 dbl1 = Array(1, 2)
 dbl2 = Array(1, 2)
と書けないのは自明ですよね。



もしも
 nd.Data = Array(1.2, 3.4)

 nd.Data(0) = 1.2
 nd.Data(1) = 3.4
の両方を可能にしたいのであれば、
Data プロパティの定義をそのように書き換えるか、
もしくは一括設定用に ParamArray 引数なメソッドを追加するなどしてみてください。
引用返信 編集キー/
■99811 / inTopicNo.6)  Re[4]: クラスが作れません。
□投稿者/ 魔界の仮面弁士 (3388回)-(2022/06/07(Tue) 16:17:13)
No99810 (魔界の仮面弁士) に追記
> もしも
>  nd.Data = Array(1.2, 3.4)
> と
>  nd.Data(0) = 1.2
>  nd.Data(1) = 3.4
> の両方を可能にしたいのであれば、
> Data プロパティの定義をそのように書き換えるか、

上記の両方を可能にしたサンプル。
手抜き実装ですけど。


Option Explicit
Private m_data As Variant
Private Sub Class_Initialize()
  ReDim m_data(0 To 10) As Double
End Sub
Public Property Get Data(Optional ByVal index As Variant) As Variant
  If IsMissing(index) Then
    Data = m_data
  Else
    Data = m_data(index)
  End If
End Property
Public Property Let Data(Optional ByVal index As Variant, ByVal newValue As Variant)
  If IsMissing(index) Then
    If IsArray(newValue) Then m_data = newValue
  Else
    m_data(index) = newValue
  End If
End Property


-----

Dim nd As clsNameData
Set nd = New clsNameData

'検証のため、現在のデータ型と要素数を確認しておきます。
' この段階では「Double()」「0」「10」と表示されます。
Debug.Print TypeName(nd.Data), LBound(nd.Data), UBound(nd.Data)

'★1:添字を指定して、メンバーを個別に代入できます。
' この時、配列のデータ型に合わせて暗黙の型変換が発生します。
nd.Data(0) = 123.456! 'Single 型の 123.456 ではなく、Double 型の 123.456001281738 が代入される
nd.Data(1) = "125D-1" 'String 型の 125D-1 ではなく、Double 型の 12.5 が代入される

Stop

'★2:添字を指定していない場合は、配列全体を一括代入できます。
' 場合によっては、データ型やメンバー数のチェックも行った方が良いでしょう。
nd.Data = Array(1, 2, 3, , 5)

'現在はデータ型やメンバー数をノーチェックで差し替えているため、
' 「Variant()」「0」「4」に変化してしまいました。
Debug.Print TypeName(nd.Data), LBound(nd.Data), UBound(nd.Data)

Stop

'★3:添字を指定して、メンバーを個別に代入できます。
' 先の1と同じ処理ですが、配列のデータ型が変化したため、先ほどと異なる結果になります。
nd.Data(0) = 123.456! 'Single 型の 123.456 がそのまま代入される
nd.Data(1) = "125D-1" 'String 型の 125D-1 がそのまま代入される



あくまでも「data(0 To 10) As Double」な形を維持したいのであれば、
Property Let の段階で「If IsArray(newValue) Then m_data = newValue」という
『配列全体の差替え行為』ではなく、個々の「配列の要素を差し替え」るようにします。
引用返信 編集キー/
■99812 / inTopicNo.7)  Re[5]: クラスが作れません。
□投稿者/ 魔界の仮面弁士 (3389回)-(2022/06/07(Tue) 16:23:19)
No99811 (魔界の仮面弁士) に追記
> あくまでも「data(0 To 10) As Double」な形を維持したいのであれば、
> Property Let の段階で「If IsArray(newValue) Then m_data = newValue」という
> 『配列全体の差替え行為』ではなく、個々の「配列の要素を差し替え」るようにします。


No99811 の手抜き実装から発展して、要素単位で差し替えるパターン。
この場合は、「data(0 To 10) As Double」を維持できます。

外部から見た場合、Data の宣言が Variant になるため、Double 配列に見えないのが難点ですが。


Option Explicit
Private m_data(0 To 10) As Double
Public Property Get Data(Optional ByVal index As Variant) As Variant
  If IsMissing(index) Then
    Data = m_data
  Else
    Data = m_data(index)
  End If
End Property
Public Property Let Data(Optional ByVal index As Variant, ByVal newValue As Variant)
  If IsMissing(index) Then
    If IsArray(newValue) Then
      Dim n As Integer, vnt As Variant
      n = 0
      For Each vnt In newValue
        On Error Resume Next
        m_data(n) = vnt
        On Error GoTo 0
        n = n + 1
        If n > 10 Then Exit For
      Next
    End If
  Else
    m_data(index) = newValue
  End If
End Property
引用返信 編集キー/
■99813 / inTopicNo.8)  Re[3]: クラスが作れません。
□投稿者/ 魔界の仮面弁士 (3390回)-(2022/06/07(Tue) 18:01:45)
No99809 (白音 さん) に返信
> プロパティの使い方がわかりません。
> Dim mdata As clsNameData
> Set mdata = New clsNameData
> call mdata.Data (0, 0)

プロパティの呼び出しに Call ステートメントは使えないのです。
Call はメソッドを呼びだす場合の構文ですね。


引数付きプロパティに対する Let 処理というのは、
 Sheet1.Range("A1") = "Example"
とか
 Sheet1.Cells(2, 3) = 4
と同じ構文で呼び出せます。

すなわち、
 mdata.Data(0) = 0
のように記述します。



> 「プロパティの使い方が不正です。」
> となってしまいます。

Range や Cells への代入処理を、Call を使った
 Call Sheet1.Range("A1", "Example")

 Call Sheet1.Cells(2, 3, 4)
などと書けないのと同じ理由です。



それとこれは今更なのですが、最初の質問時の「分類」の選択が『.NET 全般』なのは
変更漏れですよね。次回以降ご留意ください。

たとえば『VB6 以前』などでも良いですが、現状の VBA バージョンは 7.1 なので
VBA ネタの場合は『Microsoft Office 全般』あたりを選んでおくのが良いと思います。
(ちなみに VB6 のクラスモジュールと VBA のクラスモジュールでは、仕様が若干異なります)
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -