t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

オブジェクト指向でつまづく7つのポイントと処方箋

こちらの記事で伊藤先生も書かれているように、この数年でVBAのクラスモジュールに関する情報が充実してきている。

これからクラスモジュールについて学習しようと考えている方も沢山(期待を込めて)いらっしゃることと思う。

そこで今回は、オブジェクト指向を学習するにあたってつまづきやすいポイントを紹介し、その処方箋としてアドバイスを書いてみようと思う。

私がオブジェクト指向でつまづきやすいポイントは以下の7つだと考える。

  1. オブジェクト指向の目的が理解できない
  2. オブジェクト指向の悪評に惑わされる
  3. オブジェクト指向の用語がややこしすぎる
  4. オブジェクト指向の前に覚えるべきことを覚えていない
  5. オブジェクト指向の一部の特長を過大に受け止めてしまう
  6. オブジェクト指向での具体的な実装方法が分からない
  7. オブジェクト指向の活用方法が分からない

これらは私自身がつまづいたポイントでもある。こうしてみると結構ある。
では、具体的にどういうことなのか一つずつ説明していく。

オブジェクト指向の目的が理解できない

症状

初心者向けの解説でオブジェクト指向を「たい焼き」とか「動物」に例えて説明されているケースがある。
喩えを持ち出すことは悪いことではないけれど、私自身、それが業務のプログラムにどう繋がるのかサッパリ理解できなかった。
Animalクラスから継承させたDogやCatにワンニャンさせてみたところで、それが何かの役に立つとは思えないのだ。

処方箋

まずオブジェクト指向の最大の恩恵は、データや命令をセットにして扱えることであり、それによりコードをスッキリと分かりやすく記述できることである。
オブジェクト指向はコードを整理整頓する技術であり、それが根本の目的と言っても過言ではない。
何かすごいことをする技術だと思って手を出したけど今一つメリットが分からない、今までの知識でも事足りると感じているとしたら、それはある意味正しい。
散らかった部屋でも人は生きていけるように、コードは汚くても動く。
オブジェクト指向の恩恵は、コードを読んで理解し、メンテナンスする際に初めて実感できるだろう。

オブジェクト指向の悪評に惑わされる

症状

私が専門学校でJavaやVB.Netを習っている頃は、まだまだオブジェクト指向をDisる現役プログラマが沢山いた。オブジェクト指向は高度な考え方であり、そんなものを現場に持ち込んだら破綻する、と。当時の開発現場では使用する言語はJavaでも、中身はCOBOLさながらのゴリゴリの手続き型ロジックで書かれていることが多いと書かれた記事を読んだことを覚えている。
人間、一度身に着けた知識はなるべく長く使い回したいもので、ある程度年齢を重ねてから新しい手法を受け入れるのはとても勇気がいることだ。

処方箋

これは今の若手には関係ない話だと思う。VBA界隈を別としてオブジェクト指向は当たり前のように使われており、その有用性を否定する声は聞かなくなった。むしろオブジェクト指向はもう古い、これからは関数型だなどという意見も耳にするようになったけれど、これを真に受けてオブジェクト指向はもはや用済みだと考えるのは間違い。「オブジェクト指向はもう普遍化しており、今更取り立てて騒ぐようなものではない(つまりプロの開発者なら常識的に身に着けていること)、これから大きく取り上げるべきは関数型だ。」という解釈が正しいと思う。

オブジェクト指向の用語がややこしすぎる

症状

オブジェクト指向の言葉はややこしい。まず「オブジェクト」と「指向」さえもハッキリと分からないという方もいると思う。そのうえフィールドだのメソッドだのインスタンスだのと横文字が並んでいてますます混乱を増長しているようだ。さらに継承・委譲・多態性などという耳慣れない日本語まで登場する。

処方箋

これはオブジェクト指向について詳しく調べている際に陥る罠である。調べれば調べるほど、芋づる式に新しい言葉が出てきてますます混乱する。
しかし、実はVBAに限定すれば、覚えるべき言葉は限られる。例えば「継承」はVBAには存在しないので急いで覚える必要はない。
ここにVBA使いがオブジェクト指向に手を出すにあたって必ず覚えて欲しい言葉と、任意で覚えると良い言葉、覚えなくても何とかなる言葉、むしろ覚えない方が良い言葉の4つに分類しておく。

必ず覚えてほしい言葉 オブジェクト・クラス・インスタンス・プロパティ・メソッド
任意で覚えると良い言葉 メンバー・フィールド・隠蔽・アクセサ・カプセル化・コンストラクタ・デストラクタ
覚えなくてもVBAでは何とかなる言葉 継承・多態性・指向・インターフェース・デザインパターン
VBAではむしろ覚えない方が良い言葉 メッセージ

覚えない方が良い言葉というのは少々言い過ぎかもしれないが、VBAにおけるオブジェクト指向において、メッセージという言葉は不要な混乱を招く。
実はオブジェクト指向には2系統の思想があり、メッセージを扱うのはVBAとは別のオブジェクト指向体系である。従ってメッセージという言葉が登場する解説はVBA使いにはオススメしない。

オブジェクト指向の前に覚えるべきことを覚えていない

症状

なんとなくクラスモジュールに手を出してみたものの、プロシージャ?モジュールレベル変数?引数?戻り値?何それ状態。

処方箋

クラスモジュールを使用するにはプロシージャの分割やモジュールレベル変数、プロシージャ間の値の引き渡し等の前提知識が必要となる。それらの知識が無い場合はまずそこから学習が必要である。VBAも最近は良書が揃っているので自分が理解できるレベルの本で少しずつレベルアップすると良いと思う。
VBAと比べると他の言語は文法の解説書が充実しているので、VBAの書籍で今一つ理解できない場合は、プログラミングの基礎を身に着けるために一旦他の言語に手を出してみるというのも良い経験になると思う。

オブジェクト指向の一部の特長を過大に受け止めてしまう

症状

手続き型しか知らないプログラマがオブジェクト指向を獲得すると、まるで人類の英知に触れたかのような気持ちになり、オブジェクト指向のすばらしさについて語り歩くようになる。特に、オブジェクト指向の3大要素「カプセル化、継承、ポリモーフィズム」と、「いくらでもインスタンスが作れる」というオブジェクト指向の特長が強力なメリットとして強く語られる。また、カプセル化 = 隠蔽という認識が広まっており、オブジェクト指向本来のメリットである「データや命令をセットで扱う」という特長(本来のカプセル化)が霞んでしまっているのはとても残念だ。
結果的にオブジェクト指向は「難しくて大層なモノ」と誤解され、初心者から敬遠されているように思える。

処方箋

オブジェクト指向は単にデータや命令をセットにしてひと塊で扱うための整理整頓の技術である。だからその目的で気軽に使って良い。
なんならデータだけしか使わずにユーザー定義型と同じような使い方だってアリだし、インスタンスを一つしか使わなくても構わない。現にインスタンスが一つであることを保証するテクニック(シングルトン)まで登場しているくらいだ。
私は長らく、「たい焼き」のせいで、「複製しなければオブジェクト指向を採用する意味はない」と誤解していた。オブジェクト指向はもっと気軽に使って良い。

参考記事

thom.hateblo.jp

オブジェクト指向での具体的な実装方法が分からない

症状

私がVBAでオブジェクト指向を採用しようと思ったとき、クラスモジュールの使い方に関する説明は殆ど無かった。よって実際にクラスを作成する段階での記述方法について、思考錯誤しながらとても苦労した覚えがある。

処方箋

今は良書が揃っていてWebサイトでの解説も色々出そろっているのでそちらを参照すると良い。

参考記事

thom.hateblo.jp

オブジェクト指向の活用方法が分からない

症状

クラスモジュールの作り方は分かったが、それを活用する具体的なアイデアが思い浮かばない。

処方箋

これについては当ブログでも色々と記事を書いたので参照していただけると幸いである。
代表的なものは以下。
thom.hateblo.jp

それと、技術が無いとアイデアはなかなか湧いてこない。活用方法の前に、そもそもクラスモジュールの使い方をマスターしているかもう一度学習してみるのも良いと思う。
これは道具が先か、それを使うシチュエーションが先かという問題である。先に道具を知っているからこそ、適切なシチュエーションに遭遇した際に、「ここで使うと良いんじゃないか」と分かる。道具を知らないと、非効率なシチュエーションに何度出くわしても、それが普通のことだと思って見過ごしてしまう。
だからまずは道具であるクラスモジュールについてきちんと学習しておくことが大事。
使いどころは、強引にでも使っているうちに、だんだんわかってくる。

まとめ

オブジェクト指向は分かってしまえばそんな大層な技術ではないのだが、いかんせん学習における罠が多い。オブジェクト指向の学習の成否はいかにワナにハマらないかにかかっていると言っても過言ではない。
これから学習される方は、この7つの罠を注意深く避けて学習を進めるようにしていただけるとスムーズにオブジェクト指向に入門できると思う。

VBA 環状矢印を複数個つなげてリング状にするマクロ

オートシェイプで矢印を綺麗な環状に並べたい場合がある。
f:id:t-hom:20190129212208p:plain

その名の通り「環状矢印」というシェイプを使うのだが、手でやるとなかなか綺麗にできない。
試しに手でやってみたのがこちら。。
f:id:t-hom:20190129212624p:plain

ひどい。

そこで今回は環状矢印を綺麗に並べるためのマクロを作成してみた。

コード

Enum CircularArrowAdjustmentItemValues
    eThickness = 1
    eStartDegree = 4
    eEndDegree = 3
    eArrowheadSize = 5
End Enum
Type Position
    x As Double
    y As Double
End Type

Sub Draw360DegreeCircularArrows()
    '固定値:さわるな危険
    Const START_DEGREE_OFFSET = -90
    Const END_DEGREE_OFFSET = -110
    
    '設定値:適宜変更よろしく
    Const RING_SIZE = 300 'ここで指定したリングサイズになる。
    Const ARROW_THICKNESS = 0.1 '矢印の太さ。0~0.2くらいまでを、少数で指定する。
    Const NUMBER_OF_ARROWS = 5 'ここで指定した数の矢印が書かれる。12くらいが限界。
    Dim pos As Position
        pos.x = 100
        pos.y = 100
    
    'メインコードここから
    Dim startDegree As Long
    Dim endDegree As Long
    Dim i As Long
    For i = 1 To NUMBER_OF_ARROWS
        Dim sh As Shape: Set sh _
            = ActiveSheet.Shapes.AddShape(msoShapeCircularArrow, pos.x, pos.y, RING_SIZE, RING_SIZE)
        With sh
            startDegree = endDegree
            endDegree = startDegree + 360 / NUMBER_OF_ARROWS
            .Fill.ForeColor.RGB = GetRandomColor
            .Line.Visible = msoFalse
            .Adjustments.Item(eThickness) = ARROW_THICKNESS
            .Adjustments.Item(eStartDegree) = startDegree + START_DEGREE_OFFSET
            .Adjustments.Item(eEndDegree) = endDegree + END_DEGREE_OFFSET
            .Adjustments.Item(eArrowheadSize) = .Adjustments.Item(eThickness)
        End With
    Next
End Sub

Function GetRandomColor()
    Dim r: r = WorksheetFunction.RandBetween(0, 255)
    Dim g: g = WorksheetFunction.RandBetween(0, 255)
    Dim b: b = WorksheetFunction.RandBetween(0, 255)
    GetRandomColor = RGB(r, g, b)
End Function

実行方法

Draw360DegreeCircularArrowsを実行すると冒頭の図のように矢印が綺麗に環状に並ぶ。
定数で指定されている設定値を変えることで違った結果になる。

実行結果サンプル

NUMBER_OF_ARROWSに12を指定したのがこちら。
f:id:t-hom:20190129214035p:plain

ARROW_THICKNESSを0.05に指定したのがこちら。
f:id:t-hom:20190129214255p:plain

NUMBER_OF_ARROWSとARROW_THICKNESSはバランスを取る必要があり、太い矢印で個数を増やしすぎるとバグる。
ARROW_THICKNESSが0.2、NUMBER_OF_ARROWSが12の結果がこちら。
f:id:t-hom:20190129214722p:plain
なぜか真ん中に円が。

ARROW_THICKNESSを0.5にしてみると。。。風車みたいになった。
f:id:t-hom:20190129214808p:plain

ARROW_THICKNESSが0.2でも、NUMBER_OF_ARROWSが少ないとサマになる。
f:id:t-hom:20190129215112p:plain

角度補正について

START_DEGREE_OFFSETがマイナス90度、END_DEGREE_OFFSETがマイナス110度となっている。
実は矢印の頭のサイズ分、.Adjustments.Item(eStartDegree)と.Adjustments.Item(eEndDegree)では角度の開始値がズレているためだ。
f:id:t-hom:20190129215847p:plain

このままではとても分かりにくいので、両方とも時計の12時を0度として時計回りの角度で指定できるようにするためにオフセット値を引いて角度を補正している。

以上。

仕事の話 属人化とロールについて

仕事をまわしていく中で、属人化がリスクであるということがよく言われる。
属人化と聞くと、まさに「この人しかできない」「この人しか分からない」という状況をイメージされると思う。

私が思う属人化はちょっとこれとは違う。
ある仕事について、きちんとロール(役割)が定義され、マニュアルによって標準化されていれば、たとえそのロールに1名しか割り当てられていなくても属人化とは言わない。
逆に、いくら業務シェアされて複数名の担当が居たところで、仕事が標準化されていなければそれは属人的だと言える。

私のイメージする属人化

業務シェアはできている。でも仕事が人に属していて標準化されていないので属人的。
f:id:t-hom:20190127133650p:plain

私のイメージする標準化

仕事に対し、それを担当すべきロールが定義され、ロールに対して人がアサインされている状態。
f:id:t-hom:20190127133750p:plain

特に組織の変更や業務の移管が行われる際に、ロールがきちんと定義されていないと困ったことになる。業務を整理したいのであなたが対応している仕事を洗い出してください。その仕事は、なぜあなたが対応しているんですか?と聞かれたとき、例えば、その仕事はロール2が対応すると定義されていて、私はロール2も兼任している為ですと言えればOK。逆に、よくわからないけど前任者から引き継いだという答えだったら、まずはロールの定義が必要かと思われる。

VBA マクロでExcelのシェイプを旋回運動させる

こちらの記事に触発されて、ちょっと昔やった別の方法をご紹介。
infoment.hatenablog.com

参照元はとても王道的に計算してるんだけど、私のは手抜き。

コードはこちら。

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Sub FakeCircling()
    Dim sh As Shape
    Set sh = Sheet1.Shapes.AddShape(msoShapeOval, 100, 50, 10, 10)
    Dim shadow As Shape: Set shadow = sh.Duplicate
    shadow.Visible = msoFalse
    shadow.Left = sh.Left
    shadow.Top = sh.Top
    shadow.IncrementTop 100
    Dim sh2 As Shape
    Set sh2 = Sheet1.Shapes.Range(Array(sh.Name, shadow.Name)).Group
    Do
        Sleep 1
        Application.ScreenUpdating = False
        sh2.IncrementRotation 5
        Application.ScreenUpdating = True
        DoEvents
    Loop
End Sub

ちゃんと動作してる。
f:id:t-hom:20190121232743g:plain

何してるかというと。。
f:id:t-hom:20190121232912g:plain

対角線上に見えない影シェイプをつくってグルーピングしてRotate命令で回してる。

VBA Win32APIを使って、文字列の実描画幅を取得する。

PCのフォントには固定幅のものと可変幅のものがある。

固定幅のフォントは単純に同じ文字数で改行すれば綺麗な矩形になるが、可変幅のフォントでは単純に同じ文字数で改行してもガタガタになる。

f:id:t-hom:20190119014553p:plain

これを概ね揃うようにWin32 APIを使って、文字列の幅を測りながら出力してみようと思う。

きっかけになったのはタイムラインに流れてきたこちらのツイート。


コード

標準モジュールに以下のコードを張り付ける。

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type

Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hgdiobj As Long) As Long
Private Declare Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long

Private Declare Function CreateFont Lib "gdi32" Alias "CreateFontA" (ByVal nHeight As Long, _
    ByVal nWidth As Long, _
    ByVal nEscapement As Long, _
    ByVal nOrientation As Long, _
    ByVal fnWeight As Long, _
    ByVal IfdwItalic As Long, _
    ByVal fdwUnderline As Long, _
    ByVal fdwStrikeOut As Long, _
    ByVal fdwCharSet As Long, _
    ByVal fdwOutputPrecision As Long, _
    ByVal fdwClipPrecision As Long, _
    ByVal fdwQuality As Long, _
    ByVal fdwPitchAndFamily As Long, _
    ByVal lpszFace As String) As Long
    
Private Declare Function DrawText Lib "user32" Alias "DrawTextA" (ByVal hdc As Long, _
    ByVal lpStr As String, _
    ByVal nCount As Long, _
    lpRect As RECT, _
    ByVal wFormat As Long) As Long

Private Const FW_NORMAL = 400
Private Const FW_BOLD = 700
Private Const DEFAULT_CHARSET = 1
Private Const OUT_DEFAULT_PRECIS = 0
Private Const CLIP_DEFAULT_PRECIS = 0
Private Const DEFAULT_QUALITY = 0
Private Const DEFAULT_PITCH = 0
Private Const FF_SCRIPT = 64
Private Const DT_CALCRECT = &H400

Function MeasureTextWidth( _
        target_text As String, _
        FONT_NAME As String, _
        Optional font_height As Long = 10) As Long
    
    Dim hWholeScreenDC As Long: hWholeScreenDC _
        = GetDC(0&)
    
    Dim hVirtualDC As Long: hVirtualDC _
        = CreateCompatibleDC(hWholeScreenDC)

    Dim hFont As Long: hFont _
        = CreateFont(font_height, 0, 0, 0, FW_NORMAL, _
            0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, _
            CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, _
            DEFAULT_PITCH Or FF_SCRIPT, FONT_NAME)
            
    Call SelectObject(hVirtualDC, hFont)
    
    Dim DrawAreaRectangle As RECT
    Call DrawText(hVirtualDC, target_text, -1, DrawAreaRectangle, DT_CALCRECT)
    
    Call DeleteObject(hFont)
    Call DeleteObject(hVirtualDC)
    Call ReleaseDC(0&, hWholeScreenDC)
    MeasureTextWidth = DrawAreaRectangle.Right - DrawAreaRectangle.Left
End Function

Sub 幅を揃えて出力()
    Const 基準テキスト = "固定幅のフォントは"
    Const 対象テキスト = "固定幅のフォントは単純に同じ文字数で改行すれば綺麗な矩形になるが、可変幅のフォントでは単純に同じ文字数で改行してもガタガタになる。"
    Const FONT_NAME = "MS Pゴシック"
    
    Dim 基準テキストの長さ As Long
    基準テキストの長さ = MeasureTextWidth(基準テキスト, FONT_NAME)
    
    Dim tmpText As String
    tmpText = ""
    Dim i As Long
    For i = 1 To Len(対象テキスト)
        If MeasureTextWidth(tmpText + Mid(対象テキスト, i, 1), FONT_NAME) > 基準テキストの長さ Then
            Debug.Print tmpText
            tmpText = Mid(対象テキスト, i, 1)
        Else
            tmpText = tmpText & Mid(対象テキスト, i, 1)
        End If
    Next
    Debug.Print tmpText
End Sub

メインのプロシージャは「幅を揃えて出力」で、それ以外はほぼWin32APIの処理。
ちなみに64bit版Officeには対応させていない。※面倒くさかったので。。

MeasureTextWidthはテキストとフォント名を与えるとLong型で幅を返す。
※幅の単位は不明だけど、恐らくポイントだと思われる。

そしてメインの「幅を揃えて出力」では、対象テキストを1文字ずつtmpTextにくっつけて、基準となるテキスト幅を超える手前でPrintする処理となっている。

実行結果はイミディエイトウインドウに出力される。(この時点ではイミディエイトウインドウのフォント設定によるので、揃ってない。)
f:id:t-hom:20190119020411p:plain

テキストボックス等に貼り付けて対象フォントをFONT_NAMEと同じものを選択すると、このようにある程度揃う。
f:id:t-hom:20190119020518p:plain

以上

基本情報技術者の受験に役立つ、過去に書いたVBA記事の一覧をまとめてみた。

f:id:t-hom:20190105220644p:plain
新年の初投稿で書き忘れてたので。

さて、新年と言えば去年はこのような記事を書いた。
thom.hateblo.jp

節目のブースト効果で、今年こそは何か資格を取ろうとか、色々考えている方もいると思う。
私が購読しているブログでも新年の意気込みが。

今さらながら基本情報でもちょっと勉強してみようかしら。
(絶対に合格してやるぜ(炎) thom訳)

自作WindowsAPIクラスを修正した - 素人がExcelVBAによる「一人働き方改革」に挑むブログ

これを見て、そういえば基本情報技術者の学習に役立ちそうなVBA記事を過去にいくつか書いたのを思い出したので、ここに纏めておこうと思う。
プログラマーの登竜門と言われる基本情報だけど、真面目に学習すればコンピューターサイエンスの基礎が身につくのでノンプログラマーがステップアップするのにもオススメの資格である。

基本情報技術者の対策に役に立つかもしれない記事

真理値表

thom.hateblo.jp

メモリについていろいろ

thom.hateblo.jp
thom.hateblo.jp
thom.hateblo.jp

連結リスト

thom.hateblo.jp

木構造の探索

thom.hateblo.jp

指数表記

thom.hateblo.jp

加算器

thom.hateblo.jp

スタック

thom.hateblo.jp

テスト

thom.hateblo.jp

BNF(少し触れてます)

thom.hateblo.jp

逆ポーランド記法(かすってます)

thom.hateblo.jp

以上

VBA Dir関数とFileSystemObjectの比較

※ 冗談の通じない方はご退場願います。

はじめに私の立場を明確にしておくと、私は個人的にFileSystemObject(以降FSOと記載)が好きで、Dir関数はあまり好きではない。従ってDir関数で事足りる処理でも基本的にVBAコードではFSOを利用する。この記事ではその理由を述べ、Dir派の方をFSO派に改宗させることを目的としたいと思う。悪を滅し、正義の光あらんことを。なんつって。

さて、まずはDir関数がいかに貧相であるかをご覧いただきたい。Dirで出来ることは主に以下の三つである。

  1. 指定したファイル・フォルダの存在チェック
  2. 指定したパターンにマッチするファイル名の取得
  3. 指定したフォルダ内のサブフォルダ・ファイルの一覧取得

機能豊富なFSOにとってこれしきの事は朝飯前である。
FSOが如何に機能豊富であるかはFSOが備える以下のメソッド群を見てもらえば一目瞭然。

  1. BuildPath メソッド
  2. CopyFile メソッド
  3. CopyFolder メソッド
  4. CreateFolder メソッド
  5. CreateTextFile メソッド
  6. DeleteFile メソッド
  7. DeleteFolder メソッド
  8. DriveExists メソッド
  9. FileExists メソッド
  10. FolderExists メソッド
  11. GetAbsolutePathName メソッド
  12. GetBaseName メソッド
  13. GetDrive メソッド
  14. GetDriveName メソッド
  15. GetExtensionName メソッド
  16. GetFile メソッド
  17. GetFileName メソッド
  18. GetFolder メソッド
  19. GetParentFolderName メソッド
  20. GetSpecialFolder メソッド
  21. GetTempName メソッド
  22. MoveFile メソッド
  23. MoveFolder メソッド
  24. OpenTextFile メソッド

とはいえVBAの組み込み関数であるDirは特に設定なしで使えるのでこの点FSOは分が悪い。FSOを使用するには原則、参照設定という準備が必要なのだ。参照設定なしで使うとインテリセンスが効かずコーディング効率が著しく低下する。

まぁそもそもDirは単体ですべてのファイル操作を担っているわけではなく、同じFileSystemモジュールに以下の兄弟関数がいる。機能数という意味で比較対象にするなら以下も含めてやらないと不公平というものだろう。

  1. ChDrive 関数
  2. CurDir 関数
  3. CurDir$ 関数
  4. Dir 関数
  5. EOF 関数
  6. FileAttr 関数
  7. FileCopy 関数
  8. FileDateTime 関数
  9. FileLen 関数
  10. FreeFile 関数
  11. GetAttr 関数
  12. Kill 関数
  13. Loc 関数
  14. LOF 関数
  15. MkDir 関数
  16. Reset 関数
  17. RmDir 関数
  18. Seek 関数
  19. SetAttr 関数

しかし、ここで一つ注目していただきたい。FSOのメソッド名は英語としてそのまま意味が通じるのに対し、関数群の方は略記が多く、意味と名前の対応付けを、いちいち覚えておかないといけないのだ。他人がコードを読むときも、関数群の読みやすさは読み手の知識に左右される。対してFSOは簡単な英単語さえ知っていれば、おおよそ何をしているのか想像がつく。処理と名前が綺麗に結びついているからだ。

次にDir関数単体の機能と、FSOでそれに該当する機能を比べてみよう。

指定したファイル・フォルダの存在チェック

Dir関数では

次のように記載する。
Dir([ファイルパス])
Dir([フォルダパス])

ファイルやフォルダが存在した場合はファイル名やフォルダ名を返し、存在しない場合は空文字「""」を返す。このようなルールを、コードを書く人・読む人両方が覚えておかなければならない。

FSOでは

次のように記載する。
fso.FileExists([ファイルパス])
fso.FolderExists([フォルダパス])
ファイルやフォルダが存在した場合はTrueを返し、存在しない場合はFalseを返す。単純明快である。

比較結果

あるか・ないかを調べたいのだから、欲しい返事は「ある/ない」の二択だ。
VBAに置ける二択といえばブーリアン型。「True/False」で返してくれるFSOの方がシンプルで分かりやすい。
そして、プログラムを読む際も「単一パスの存在チェックをしていることが明示的」であるFSOに分がある。

指定したパターンにマッチするファイル名の取得

Dir関数では

次のように記載する。
Dir([パターン])
ここで[パターン]は「*」や「?」などのワイルドカードを含むパス名だ。これもルールを人が覚える必要がある。

また、この記述で返ってくるファイル名は1つだけだ。
2番目にマッチしたファイル名を返すには、もう一度Dirを引数無しで呼び出す必要がある。
そしてDirはすべてのマッチしたファイル名を返し終わると最後に空文字を返す。
このような煩雑なルールを覚えなければならないのだ。

FSOでは

FSOでは一括パターンマッチは出来ない。したがって、まずパス配下のファイルの一覧を取得することになる。
fso.GetFolder([フォルダパス]).Files

GetFolderにより取得したフォルダーオブジェクトのFilesメソッドはFilesというコレクションを返すので、Dirのように何度も呼び出す必要はない。
取得したら、For EachなどでそれぞれIf文による判定を行い、合致するパターンのファイルを個別により分けていく。

比較結果

ここでもDirは知識を要するのに対し、FSOは明示的で分かりやすいコードになる。

指定したフォルダ内のサブフォルダ・ファイルの一覧取得

Dir関数では

次のように記載する。
Dir([フォルダパス\*])
Dir([フォルダパス\*], vbDirectory)

ただし、属性にvbDirectoryを指定しても、ファイルも含めて返してくる。
これはvbNormalの実値が0で、vbDirectory(16) + vbNormal(0)と同じ判定結果になるためだ。
実際にフォルダかどうかを調べるにはGetAttr関数で返ってきたパスがフォルダーかどうか検証しなければならない。

なお、こちらも一度に返せるファイル名・フォルダ名はひとつなのでDirを引数無しで何度も呼び出す必要がある。

FSOでは

FSOでは次のように直感的に意味の分かる書き方ができる。
fso.GetFolder([フォルダパス]).Files
fso.GetFolder([フォルダパス]).SubFolders

それぞれの戻り値もFilesコレクション・Foldersコレクションで、For Eachループでスマートに処理できる。

まとめ

リーダブルコードを心がけるうえで、Dirはもはや害悪でしかない。今こそDir関数に正義の鉄槌を!FSO万歳。

注意)個人の感想です。随所に偏見が見られますので鵜呑みにしないようにお気をつけください。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。