7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事誰得? 私しか得しないニッチな技術で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

【COM】【Excel】`Workbook.Worksheets`プロパティが返す型は`Worksheets`じゃない

Last updated at Posted at 2024-06-24

要旨

Excel VBAのWorkbook.Worksheetsプロパティのドキュメントを見ると、このように書かれています。

指定したブック内のすべてのワークシートを表す Worksheets コレクションを返します。 1

しかし、そうではない事実がコードを実行すると確認できます。

VBAでの再現

Worksheetsプロパティが返す型を確認するため、下記のVBAコードを実行してみます。

Public Sub VBASample()

    Dim app As Excel.Application
    Dim wb As Excel.Workbook
    
    Set app = New Excel.Application
    app.Visible = True
    Set wb = app.Workbooks.Add
    Debug.Print "Type of Worksheets: ", TypeName(wb.Worksheets)
    app.Quit

End Sub

すると、Worksheetsプロパティは実際にはSheets型を返していることがわかります。

Type of Worksheets:         Sheets

Sheetsオブジェクト2Worksheetsオブジェクト3のエイリアスというわけではなく、別のオブジェクトです。

SheetsコレクションはWorksheet4Chart5 (そしてシートを右クリックしたメニューの「挿入」から追加できるExcel5.0ダイアログシートやExcel4.0マクロシートも含む)をアイテムとして扱えますが、WorksheetsコレクションはWorksheetしかアイテムとして扱えません。

ワークシートとチャートをひっくるめて扱っているSheetsプロパティ6Sheets型を返して型名とプロパティ名が一致しています。
一方でワークシートだけ扱うWorksheetsプロパティがWorksheets型ではなくSheets型なのはなかなかにややこしいです。

    Debug.Print "Type of Sheets: ", TypeName(wb.Sheets)
Type of Sheets:             Sheets

備考: Workbook.Chartsプロパティの場合

ちなみにChartsプロパティがどうかというと、これもSheets型です。

    Debug.Print "Type of Charts: ", TypeName(wb.Charts)
Type of Charts:             Sheets

しかしこちらについてChartsプロパティのドキュメント7にはSheetsコレクションを返すと書いてある分、まだ親切です。

指定したブック内のすべてのグラフ シートを表す Sheets コレクションを返します。

Pythonとcomtypesでの再現

COM型ライブラリ情報に基づいて、COMインターフェースをPythonのクラスとして定義したモジュールを生成できるcomtypes8でもこの挙動は再現されます。

>>> from comtypes.client import CreateObject 
>>> app = CreateObject("Excel.Application")
>>> app.Visible = True
>>> wb = app.Workbooks.Add()
>>> print(wb.Worksheets)  # doctest: +ELLIPSIS
<POINTER(Sheets) ptr=... at ...>
>>> app.Quit()
0
>>> exit()

実行時の振る舞いについて

VBAにしてもPython+comtypesにしても、実行時にはたとえ返り値がSheets型であってもWorkbook.Worksheetsプロパティが返すコレクションの中身はWorksheetのみであり、Chartが紛れているということはありません。

これは下記のVBAコードを実行することで確認できます。

Sub VBASample2()

    ' Sheet1とChart1があるワークブックで実行
    Dim wb As Excel.Workbook
    Dim sh As Variant
    
    Set wb = ThisWorkbook
    
    For Each sh In wb.Sheets
        Debug.Print "Sheets:", sh.Name, TypeName(sh)
    Next sh
    
    Debug.Print "========"

    For Each sh In wb.Worksheets
        Debug.Print "Worksheets:", sh.Name, TypeName(sh)
    Next sh

    Debug.Print "========"

    For Each sh In wb.Charts
        Debug.Print "Charts:", sh.Name, TypeName(sh)
    Next sh

End Sub

Sheets:       Sheet1        Worksheet
Sheets:       Chart1        Chart
========
Worksheets:   Sheet1        Worksheet
========
Charts:       Chart1        Chart

この仕様が記載されているドキュメント

WorksheetsプロパティがSheets型を返す記載が全くないわけではありません。
なんとApplication.Worksheetsのドキュメント9にあります。

Workbook オブジェクトの場合は、指定したブック内のすべてのワークシートを表す Sheets コレクションを返します。

なぜこんなところに記載されているのかは想像にすぎませんがWorksheetsプロパティの仕様を書いた他のドキュメントから転用するときに紛れてしまったのかもしれません。

英語ドキュメントの構文部分には

Syntax
expression.Worksheets

があり、元になったドキュメントは汎用的にWorksheetsプロパティを説明していただろうことが推測できます。

これで困ることは何なのか

Sheets型を返すメソッドやプロパティを呼び出す際、ワークシートのコレクションなのかチャートが入るコレクションなのかを区別する必要があります。

特に、ワークシートにはセルの概念がありますが、チャートはセルを持ちません。
なので、不用意にコレクションアイテムのセルにアクセスしようとしたときに、それがチャートだとエラーを起こします。

SheetsWorksheetsは紛らわしいので、コードレビューをすり抜けてバグを生みかねません。

どう混同を防止するか

私が参画しているPythonプロジェクトでは、コア機能提供ライブラリ内にワークブックCOMインターフェースの振る舞いをラップするオブジェクトを作り、それにワークシートだけがアイテムとなるコレクションを返すプロパティを実装しています。
これによって、プロジェクトに参画した開発者がSheetsプロパティかWorksheetsプロパティどちらを使えばいいか迷うことなくワークシートを簡単に取得でき、バグが入り込みにくいようにしています。

最後に

ExcelのCOMライブラリ内にはSheetsWorksheetsのように混同しやすいものの他にも、メソッドの返り値の型について遅延評価を前提に作られている部分があり、型安全なプログラミングをやりづらい部分があります。
私はcomtypesをラップしたExcel操作オープンソースライブラリを作ろうとしていますが、それらが原因で仕様を決めかねています。
ランタイムコードを定義するのも、型スタブを定義するのも、コード量が膨大になってしまうアイディアしかまだ浮かばない状況です。
いいアイディアがあれば、この記事のコメントなどで頂けると幸いです。

  1. https://learn.microsoft.com/ja-jp/office/vba/api/excel.workbook.worksheets

  2. https://learn.microsoft.com/ja-jp/office/vba/api/excel.sheets

  3. https://learn.microsoft.com/ja-jp/office/vba/api/excel.worksheets

  4. https://learn.microsoft.com/ja-jp/office/vba/api/excel.worksheet

  5. https://learn.microsoft.com/ja-jp/office/vba/api/excel.chart(object)

  6. https://learn.microsoft.com/ja-jp/office/vba/api/excel.workbook.sheets

  7. https://learn.microsoft.com/ja-jp/office/vba/api/excel.workbook.charts

  8. https://github.com/enthought/comtypes

  9. https://learn.microsoft.com/ja-jp/office/vba/api/excel.application.worksheets

7
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?