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

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

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

Re[6]: Object型のキャスト


(過去ログ 138 を表示中)

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

■80928 / inTopicNo.1)  Object型のキャスト
  
□投稿者/ masa (10回)-(2016/08/21(Sun) 21:18:45)

分類:[VB.NET/VB2005 以降] 

Object型で受けた変数を推論された型に変換する方法を教えてください。

  Private Sub test(ByVal ParamArray v() As Object)
        Dim t0 As Type = v(0).GetType 'System.String
        Dim t1 As Type = v(1).GetType 'System.Int32
        Dim t2 As Type = v(2).GetType 'System.Double
      
        'Dim s As String = CType(v(0), t0)
        'Dim i As Int32 = CType(v(1), t1)
        'Dim d As Double = CType(v(2), t2)
  End Sub

 test("A", 1, 0.1)を実行した場合、CTypeの第2引数はどのように書けばよいのでしょうか?

引用返信 編集キー/
■80929 / inTopicNo.2)  Re[1]: Object型のキャスト
□投稿者/ masa (11回)-(2016/08/21(Sun) 21:25:51)
No80928 (masa さん) に返信
追記:
CType(v(0), System.String)いう書き方ではなく、型不明の場合でも推論型を指定できる方法を知りたいです。

引用返信 編集キー/
■80930 / inTopicNo.3)  Re[2]: Object型のキャスト
□投稿者/ Hongliang (451回)-(2016/08/21(Sun) 21:35:41)
型推論は、コンパイル時に行われるものです。
v(0)がどんな型のインスタンスなのかは実行しないと分からないので、コンパイル時には解決できず、当然型推論も不可能です。
引用返信 編集キー/
■80931 / inTopicNo.4)  Re[3]: Object型のキャスト
□投稿者/ masa (12回)-(2016/08/21(Sun) 21:59:35)
No80930 (Hongliang さん) に返信
> 型推論は、コンパイル時に行われるものです。
> v(0)がどんな型のインスタンスなのかは実行しないと分からないので、コンパイル時には解決できず、当然型推論も不可能です。

Hongliang様、返信ありがとうございます。

以下でConsole.WriteLineの時点で既に、希望の型に推論されていますが
これは、コンパイル時になるのでしょうか?

Private Sub test(ByVal ParamArray v() As Object)
        Dim t0 As Type = v(0).GetType 'System.String
        Dim t1 As Type = v(1).GetType 'System.Int32
        Dim t2 As Type = v(2).GetType 'System.Double
      

        Console.WriteLine(v(0).GetType)
        Console.WriteLine(v(1).GetType)
        Console.WriteLine(v(2).GetType)

    'Dim s As String = CType(v(0), t0)
        'Dim i As Int32 = CType(v(1), t1)
        'Dim d As Double = CType(v(2), t2)
  End Sub

引用返信 編集キー/
■80932 / inTopicNo.5)  Re[4]: Object型のキャスト
□投稿者/ Azulean (687回)-(2016/08/21(Sun) 22:42:10)
2016/08/21(Sun) 23:00:27 編集(投稿者)

No80931 (masa さん) に返信
> 以下でConsole.WriteLineの時点で既に、希望の型に推論されていますが
> これは、コンパイル時になるのでしょうか?

それは推論ではありません。
オブジェクト自身が型情報を持っており、それを取り出しているだけです。

型推論は、変数に型を明示的に指示しなかったときに、変数が何型になるかをコンパイル時に推測、決定する仕組みのことなので、実行時の Object 型の中身とか、GetType とは無関係です。


Object 型には何でも入りますが、その型が何だったかは GetType で知ることはできます。
(test という関数(メソッド)はコンパイル時点で、誰がその test をどんな風に利用するかは決まっていないので、コンパイル時に決定されません)

もっとも、そういう型情報の仕組みがあったとして、t0 〜 t2 といった実行時に中身が決まるものを CType の引数に入れることはできません。

-----
何がやりたくて質問されているのかわからないのですが、決まった型の順番で来ることが前提としているなら、単純に引数を3つ並べるか、3つのフィールドを持つクラスにした方がいいと思います。
その方がコードはシンプルになりますから。

どんなに頑張っても、引数の数の変動、型の順番違いに万能に対応するコードは書けません。
どう頑張っても、型の制約がコードで決まります。
ゆえに、Object 型の配列で受け取って型キャストを動的な仕組みを求めることに違和感を持っているからです。
引用返信 編集キー/
■80934 / inTopicNo.6)  Re[5]: Object型のキャスト
□投稿者/ masa (13回)-(2016/08/21(Sun) 23:19:46)
No80932 (Azulean さん) に返信
Azulean様、返信ありがとうございます。

了解しました。

>決まった型の順番で来ることが前提としているなら

ExcelのVBAをVB.NETから実行する為に、汎用的な関数を作成していました。
なので、引数の数も、型も実行時まで不明です。

Object型のまま実行するとエラーとなり、きちんと型変換すると希望する結果が得られたので、
このような質問となりました。

わざわざ、関数化するほどの事もないので、本件はあきらめます。
どうもありがとうございました。

引用返信 編集キー/
■80935 / inTopicNo.7)  Re[6]: Object型のキャスト
□投稿者/ Azulean (688回)-(2016/08/21(Sun) 23:32:44)
No80934 (masa さん) に返信
> ExcelのVBAをVB.NETから実行する為に、汎用的な関数を作成していました。
> なので、引数の数も、型も実行時まで不明です。

Type.InvokeMember を使っているのだとしたら、その引数の args も Object の配列なのでそこにそのまま入れられそうですが、
別の方法を使っているのでしょうか?
https://msdn.microsoft.com/ja-jp/library/66btctbe


> Object型のまま実行するとエラーとなり、きちんと型変換すると希望する結果が得られたので、
> このような質問となりました。

この路線の可能性を考えてみたいのであれば、具体例を出していただいた方が良いかと思います。
そうすれば、「こうすればできる」とか、「そのやり方はできない」とか、「似たような方法でこうならできる」とかの助言が得られるかもしれないためです。
引用返信 編集キー/
■80936 / inTopicNo.8)  Re[7]: Object型のキャスト
□投稿者/ masa (14回)-(2016/08/22(Mon) 00:09:46)
No80935 (Azulean さん) に返信
作っていたのは、以下のようなものです。

'自作のExcelClass
'xlApp:Excel.Applicationオブジェクト
'引数の数が未定だがargsを配列で渡すとエラーとなる為、1つずつ渡す。実用上はCase10くらいまであればいいかなと...
  Public Overloads Sub VBARun(ByVal BookName As String, ByVal MacroName As String, ByVal ParamArray args() As Object)
        Select Case args.Count
            Case 1 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0))
            Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0), args(1))
            Case 3 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0), args(1), args(2))
      ...
      Case 10 :.....
        End Select
    End Sub

'.NET呼び出し側
 Private TargetExcel As New ExcelClass
 Private Sub VBAマクロを実行Sub引数あり
        'Subステートメント(引数あり)を実行する場合
        'VBAの定義 Public Sub test(v1 As Integer, v2 As Integer)
        Dim bookname As String = "Personal.xls"
    Dim macroname As String = "test"
        TargetExcel.VBARun(bookname, macroname, 1, 5)

    End Sub

'VBA
Public Sub test(v1 As Integer, v2 As Integer)
 MsgBox v1 + v2
End Sub

上ではCOMExceptionが発生しクラス側で以下のように型変換するとOKでした。
Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, CInt(args(0)), CInt(args(1)))


引用返信 編集キー/
■80941 / inTopicNo.9)  Re[1]: Object型のキャスト
□投稿者/ 真田昌幸 (42回)-(2016/08/22(Mon) 09:50:02)
No80928 (masa さん) に返信
> Object型で受けた変数を推論された型に変換する方法を教えてください。
>
>   Private Sub test(ByVal ParamArray v() As Object)

まず、Object型にする必要性があるか(ExcelのRangeオブジェクト自体で渡す等があるか?)を検討なされては?
VB6やVBAではParamArrayの型はVariant型しかなかったので、
それをそのままコンバートツール等にかけると機械的にObject型に変換されることが多いはずです。

VB.netではほかの型でも確か宣言できたはずなので、
パラメタにどんな型が入りうるか検討して見直した方がよいかと思います。
汎用Object型はnullの扱いがややこしかったりするのであまりお勧めしません。
あり得るパターンが値型としての汎用でよいのか、
オブジェクト自体の汎用性が必要なのかによって、
型を決められた方がよいかと。

> test("A", 1, 0.1)を実行した場合、CTypeの第2引数はどのように書けばよいのでしょうか?

のようなパターンしか想定されないならStringで十分な気がします。

引用返信 編集キー/
■80944 / inTopicNo.10)  Re[8]: Object型のキャスト
□投稿者/ 魔界の仮面弁士 (833回)-(2016/08/22(Mon) 10:20:28)
2016/08/22(Mon) 12:11:27 編集(投稿者)

No80936 (masa さん) に返信
> Public Sub test(v1 As Integer, v2 As Integer)
> MsgBox v1 + v2
> End Sub

上記とは別に、下記の 3 つも定義してみてください。

'引数を「ByRef As 32bit整数型」にしたもの
Public Sub test2(v1 As Long, v2 As Long)
  MsgBox v1 + v2
End Sub

'引数を「ByVal As 16bit整数型」にしたもの
Public Sub test3(ByVal v1 As Integer, ByVal v2 As Integer)
  MsgBox v1 + v2
End Sub

'引数を「ByRef As バリアント型」にしたもの
Public Sub test4(ByRef v1 As Variant, ByRef v2 As Variant)
  MsgBox v1 + v2
End Sub



元の test の実装のままですと、仮に VBA 自身から利用するとしても
  Dim a As Long, b As Long
  a = 1
  b = 3
  Call test(a, b)
という呼び出しに対して、
 「ByRef 引数の型が一致しません」
というコンパイルエラーになりますよね。


> Select Case args.Count
配列なら args.Length の方が良いかも。


> Case 1 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0))
あれ…Case 0 は?


> 上ではCOMExceptionが発生し
VBA 側が、「As Int16」相当のパラメータを要求しているのに、
そこに対して「As Int32」相当の呼び出しを行っているためですね。

TargetExcel.VBARun("Personal.xls", "test", 1, 5) '型が異なるのでエラー
TargetExcel.VBARun("Personal.xls", "test", 1s, 5s) '本来はこう書くべき
TargetExcel.VBARun("Personal.xls", "test2", 1, 5) 'これなら呼べる
TargetExcel.VBARun("Personal.xls", "test3", 1, 5) 'これでも呼べる
TargetExcel.VBARun("Personal.xls", "test4", 1, 5) 'こちらも呼べる


Run メソッドを通じての呼び出しは、実行時に型が検査されるため、
いっそのことを、VBA 側を Variant で統一するのも手ではあります。


もし、VBA 側のコードを変更せず、かつ、
 TargetExcel.VBARun(bookname, macroname, 1, 5)
 TargetExcel.VBARun(bookname, macroname, "1", "5")
などの呼び出しを許容させたいのであれば、
> Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0), args(1))
の部分を
 Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, (args(0)), (args(1)))
あるいは
 Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, CObj(args(0)), CObj(args(1)))
にしてみてください。


> '引数の数が未定だがargsを配列で渡すとエラーとなる為、

ParamArray を渡したいなら、MethodInfo.Invoke を使ってみては如何でしょう。

下記では VB2015 の構文を使っていますが、それ以前のバージョンでも
MethodInfo.Invoke からの呼び出しは可能なはず。

Public Sub VBARun(bookName$, macroName$, ParamArray args() As Object)
 Dim p = New Object() {$"'{bookName}'!{macroName}"} _
    .Concat(args).Concat(Enumerable.Repeat(Of Object)( _
    Type.Missing, 30)).Take(31).ToArray()
 Me.xlApp.GetType().GetMethod("Run")?.Invoke(Me.xlApp, p)
End Sub

※x86 または 32bit優先モードでコンパイルしてください。
引用返信 編集キー/
■80945 / inTopicNo.11)  Re[9]: Object型のキャスト
□投稿者/ 魔界の仮面弁士 (834回)-(2016/08/22(Mon) 10:35:26)
2016/08/22(Mon) 12:44:49 編集(投稿者)

No80944 (魔界の仮面弁士) に追記
>  Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, (args(0)), (args(1)))

これは要するに、VBA 的に言えば
>   Dim a As Long, b As Long
>   a = 1
>   b = 3
>   Call test(a, b)
という呼び出しを
 'Call test(a, b)
 Call test((a), (b))
に変更したということです。

これにより、引数が値渡しになるため、
>  「ByRef 引数の型が一致しません」
のエラーが発生しなくなります。


> 上ではCOMExceptionが発生しクラス側で以下のように型変換するとOKでした。
> Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, CInt(args(0)), CInt(args(1)))

VBA 側が ByRef だったため、型が厳密にチェックされています。

呼び出しが値渡しになるためには、引数を変数ではなく式にすれば良いので、
実際には CInt である必要は無かったわけです。(そもそも本来は CShort)

たとえ CInt や CObj を行ったとしても、
 args(0) = CInt(args(0))
 args(1) = CInt(args(1))
 Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0), args(1))
としてしまうと、今回のケースでは同じエラーになってしまうことでしょう。
VBA 側の型に合わせて
 args(0) = CShort(args(0))
 args(1) = CShort(args(1))
 Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0), args(1))
ならば呼べますけれどね。


> ParamArray を渡したいなら、MethodInfo.Invoke を使ってみては如何でしょう。
ちなみに提示したコードでは、VBA 側が
 Public Sub test(v1 As Integer, v2 As Integer)
   MsgBox v1 + v2
   v1 = v1 + v2
 End Sub
だったとしても、呼び出し元の引数は書き変わりませんので御注意を。

一応、ByRef 対応のために ParameterModifier なるものも用意されていますが
それも手間なので、ByRef にも対応させる必要がある場合には

 '常に 31個の引数を指定し、使わない引数は Type.Missing を入れておく
 Me.xlApp.Run(マクロ名, args(0), args(1), …, args(30))

のようにしてしまった方が手っ取り早そうです。


No80929 (masa さん) に返信
> CType(v(0), System.String)いう書き方ではなく、型不明の場合でも推論型を指定できる方法を知りたいです。
強いて言えば、
 Public Sub VBARun(Of T)(ByVal BookName As String, ByVal MacroName As String, ByVal ParamArray args() As T)
ですかね。

ただ、今回のエラー要因は VBA 側の引数が Type.GetType("System.Int16&") に
相当する型になっていたため、ParamArray の型を推論したとしても
あまり意味がなさそうです。
それに上記だと args すべてに同じ型が要求されてしまうため、
実際には Object 配列のままの方が使いやすいでしょうね。
引用返信 編集キー/
■80956 / inTopicNo.12)  Re[2]: Object型のキャスト
□投稿者/ masa (15回)-(2016/08/22(Mon) 13:38:28)
魔界の仮面弁士様、詳細な解説をありがとうございます。

実行結果が違う点があるのですが.....
32bitマシンで試しました。
環境:OS:Windows Vista SP2 32bit、Excel2003,VB2008 .Net4.0

当初のコード   Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0), args(1))
引数を()で囲む Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, (args(0)), (args(1)))
CObj       Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, CObj(args(0)), CObj(args(1)))

上の3パターンですべて同じ結果になりました。
結果は以下の通りです。test2はそちらではOKみたいですが、こちらでは全滅でした。
やはり内部形式の型が同じ(VBA:Long,.Net:Int32)でも、Object型ではダメみたいです。
COMExceptionをキャッチした結果は
ErrorCode:-2146827284
Massage:マクロ ''Personal.xls'!test2' が見つかりません。

TargetExcel.VBARun("Personal.xls", "test", 1, 5) '型が異なるのでエラー===>エラー
TargetExcel.VBARun("Personal.xls", "test", 1s, 5s) '本来はこう書くべき===>エラ−
TargetExcel.VBARun("Personal.xls", "test2", 1, 5) 'これなら呼べる==−−−>エラ−
TargetExcel.VBARun("Personal.xls", "test3", 1, 5) 'これでも呼べる=====>OK
TargetExcel.VBARun("Personal.xls", "test4", 1, 5) 'こちらも呼べる=====>OK

すみません。次にPCに触れるのは夜になってしまいます。


引用返信 編集キー/
■80957 / inTopicNo.13)  Re[8]: Object型のキャスト
□投稿者/ PANG2 (140回)-(2016/08/22(Mon) 15:15:43)
2016/08/22(Mon) 15:16:51 編集(投稿者)

No80936 (masa さん) に返信
> '引数の数が未定だがargsを配列で渡すとエラーとなる為、1つずつ渡す。実用上はCase10くらいまであればいいかなと...
> Public Overloads Sub VBARun(ByVal BookName As String, ByVal MacroName As String, ByVal ParamArray args() As Object)
> Select Case args.Count
> Case 1 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0))
> Case 2 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0), args(1))
> Case 3 : Me.xlApp.Run("'" & BookName & "'!" & MacroName, args(0), args(1), args(2))
>       ...
>       Case 10 :.....
> End Select
> End Sub


代わりに、型推論のオーバーロードを十個用意するとか

Public Overloads Sub VBARun(Of T0)(ByVal BookName As String, ByVal MacroName As String, ByVal args0 As T0)
Public Overloads Sub VBARun(Of T0, T1)(ByVal BookName As String, ByVal MacroName As String, ByVal args0 As T0, ByVal args1 As T1)
Public Overloads Sub VBARun(Of T0, T1, T2)(ByVal BookName As String, ByVal MacroName As String, ByVal args0 As T0, ByVal args1 As T1, ByVal args2 As T2)
...

引用返信 編集キー/
■80958 / inTopicNo.14)  Re[3]: Object型のキャスト
□投稿者/ 魔界の仮面弁士 (837回)-(2016/08/22(Mon) 15:16:00)
No80956 (masa さん) に返信
> Excel2003
済みません、こちらには Excel 2007 未満の環境が残って無いので、
バージョン依存の問題だとしたら、ちょっと分からないかも。



> ErrorCode:-2146827284
> Massage:マクロ ''Personal.xls'!test2' が見つかりません。

これがエラーになるのであれば、まずは Public Overloads Sub VBARun を用いずに、
ExcelClass 側で直接
 Dim v1 As Short = 1s
 Dim v2 As Short = 5s
 Me.xlApp.Run("'Personal.xls'!test2", v1, v2)
を確認してみてください。
このコードでエラーになるのなら、ParamArray とは別の話ということになりますよね。


また、上記が成功するのであれば、今度は
 Dim args(1) As Objct = New Object() { 1s, 5s }
 Me.xlApp.Run("'Personal.xls'!test2", args(0), args(1))
が成功するかどうかを確認してみてください。
引用返信 編集キー/
■80960 / inTopicNo.15)  Re[9]: Object型のキャスト
□投稿者/ masa (16回)-(2016/08/22(Mon) 18:53:16)
No80957 (PANG2 さん) に返信

> 代わりに、型推論のオーバーロードを十個用意するとか
>
> Public Overloads Sub VBARun(Of T0)(ByVal BookName As String, ByVal MacroName As String, ByVal args0 As T0)
> Public Overloads Sub VBARun(Of T0, T1)(ByVal BookName As String, ByVal MacroName As String, ByVal args0 As T0, ByVal args1 As T1)
> Public Overloads Sub VBARun(Of T0, T1, T2)(ByVal BookName As String, ByVal MacroName As String, ByVal args0 As T0, ByVal args1 As T1, ByVal args2 As T2)
> ...
>
PANG2様、ありがとうございます。
こちらの方法でもOKでした。これが推論型ですか。
大変勉強になりました。
引用返信 編集キー/
■80961 / inTopicNo.16)  Re[4]: Object型のキャスト
□投稿者/ masa (17回)-(2016/08/22(Mon) 19:15:41)
魔界の仮面弁士様、大変申し訳ありません。
大チョンボをしておりました。

Personal.xlsの中に同じtest2という名前のSubステートメントがあり、それが悪さをしていたようです。
ユニークな名前のSubステートメントで実行したら、すべてそちらと同じ結果となりました。
VBAはInt16を要求しているのに呼び出し側で内部形式Int32のObject型を渡していたのが原因ですね。
ちょっと恥ずかしい失敗でした。

Case 0 の場合は
Public Overloads Sub VBARun(ByVal BookName As String, ByVal MacroName As String)
のOverloadsで対応するつもりです。

MethodInfo.Invokeについては、使ったことがないので今後、調査してみます。

とりあえず、私の大チョンボということで、お騒がせしました。



解決済み
引用返信 編集キー/
■80962 / inTopicNo.17)  Re[5]: Object型のキャスト
□投稿者/ 魔界の仮面弁士 (838回)-(2016/08/22(Mon) 20:36:01)
No80956 (masa さん) に返信
> 環境:OS:Windows Vista SP2 32bit、Excel2003,VB2008 .Net4.0

VB2008 は、CLR2(.NET 2.0〜3.5x)向けの開発ツールであり、
.NET 4 向けのコンパイルはできないと思いますよ。


No80961 (masa さん) に返信
> MethodInfo.Invokeについては、使ったことがないので今後、調査してみます。

No80944 の MethodInfo 処理を、VB2008 でも動くように書きなおしてみました。

やっていることは、指定された引数の数によらず、常に引数 31 個の
 xlApp.Run(macro, args(0), args(1),…,args(29))
を固定的に呼び出し、足りない引数を Missing で補完するというものです。


Public Sub VBARun(ByVal bookName As String, ByVal macroName As String, ByVal ParamArray args() As Object)
  Dim macro As String = String.Format("'{0}'!{1}", bookName, macroName)

  ' この書き方なら、VB2005 でも動く
  Dim p(30) As Object
  p(0) = macro
  For i As Integer = 1 To Math.Min(30, args.Length)
    p(i) = args(i - 1)
  Next
  For i As Integer = args.Length + 1 To 30
    p(i) = Type.Missing
  Next


  '' 2008 以降なら、上記を下記のように書ける
  'Dim p() As Object = New Object() {macro}
  'p = p.Concat(args).Concat(Enumerable.Repeat(Type.Missing, 30)).Take(31).ToArray()


  '' No80944 で書いた System.Reflection.MethodInfo 経由で呼び出す方法
  'Dim mi As System.Reflection.MethodInfo = Me.xlApp.GetType().GetMethod("Run")
  'If Not mi Is Nothing Then
  '  mi.Invoke(Me.xlApp, p)
  'End If

  '' でも今回は、MethodInfo を使わない方が素直かも
  Me.xlApp.Run( macro, _
         p(1) , p(2) , p(3) , p(4) , p(5) , _
         p(6) , p(7) , p(8) , p(9) , p(10), _
         p(11), p(12), p(13), p(14), p(15), _
         p(16), p(17), p(18), p(19), p(20), _
         p(21), p(22), p(23), p(24), p(25), _
         p(26), p(27), p(28), p(29), p(30) )
End Sub


ちなみに本来の Run は、ParamArray では無く Optional なので、
Sub VBARun についても、最初から Optional で宣言しておけば、
上記のように Missing を補完する必要が無くなります。
(それはそれで冗長的ですが)

また、Excel 2003 だけでなく、Excel 97 や Excel 5.0 でも
Run メソッドの引数は 31 個固定となので、上記は Excel のバージョンを問わず有効です。
(Excel バージョンによっては、LCIDConversionAttribute 付きの Run もあります)
解決済み
引用返信 編集キー/
■80965 / inTopicNo.18)  Re[6]: Object型のキャスト
□投稿者/ masa (18回)-(2016/08/22(Mon) 21:37:55)
No80962 (魔界の仮面弁士 さん) に返信

> VB2008 は、CLR2(.NET 2.0〜3.5x)向けの開発ツールであり、
3.5でした。


> No80944 の MethodInfo 処理を、VB2008 でも動くように書きなおしてみました。
>
> やっていることは、指定された引数の数によらず、常に引数 31 個の
>  xlApp.Run(macro, args(0), args(1),…,args(29))
> を固定的に呼び出し、足りない引数を Missing で補完するというものです。
>
>
> Public Sub VBARun(ByVal bookName As String, ByVal macroName As String, ByVal ParamArray args() As Object)
>   Dim macro As String = String.Format("'{0}'!{1}", bookName, macroName)
>
>   ' この書き方なら、VB2005 でも動く
>   Dim p(30) As Object
>   p(0) = macro
>   For i As Integer = 1 To Math.Min(30, args.Length)
>     p(i) = args(i - 1)
>   Next
>   For i As Integer = args.Length + 1 To 30
>     p(i) = Type.Missing
>   Next
>
>
>   '' 2008 以降なら、上記を下記のように書ける
>   'Dim p() As Object = New Object() {macro}
>   'p = p.Concat(args).Concat(Enumerable.Repeat(Type.Missing, 30)).Take(31).ToArray()
>
>
>   '' No80944 で書いた System.Reflection.MethodInfo 経由で呼び出す方法
>   'Dim mi As System.Reflection.MethodInfo = Me.xlApp.GetType().GetMethod("Run")
>   'If Not mi Is Nothing Then
>   '  mi.Invoke(Me.xlApp, p)
>   'End If
>
>   '' でも今回は、MethodInfo を使わない方が素直かも
>   Me.xlApp.Run( macro, _
>          p(1) , p(2) , p(3) , p(4) , p(5) , _
>          p(6) , p(7) , p(8) , p(9) , p(10), _
>          p(11), p(12), p(13), p(14), p(15), _
>          p(16), p(17), p(18), p(19), p(20), _
>          p(21), p(22), p(23), p(24), p(25), _
>          p(26), p(27), p(28), p(29), p(30) )
> End Sub
>
>
> ちなみに本来の Run は、ParamArray では無く Optional なので、
> Sub VBARun についても、最初から Optional で宣言しておけば、
> 上記のように Missing を補完する必要が無くなります。
> (それはそれで冗長的ですが)
>
> また、Excel 2003 だけでなく、Excel 97 や Excel 5.0 でも
> Run メソッドの引数は 31 個固定となので、上記は Excel のバージョンを問わず有効です。
> (Excel バージョンによっては、LCIDConversionAttribute 付きの Run もあります)

ありがとうございます。ばっちり動作しました。
引用返信 編集キー/


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

このトピックに書きこむ

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

管理者用

- Child Tree -